@devaloop/devalang 0.0.1-beta.1 → 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/.devalang +9 -10
- package/Cargo.toml +84 -80
- package/README.md +10 -7
- package/docs/CHANGELOG.md +83 -0
- package/docs/ROADMAP.md +6 -2
- package/docs/TODO.md +3 -14
- package/examples/bus.deva +10 -0
- package/examples/chain.deva +19 -0
- package/examples/effect.deva +2 -0
- package/examples/filter.deva +11 -0
- package/examples/lfo.deva +9 -0
- package/examples/plugin.deva +10 -10
- package/examples/routing.deva +23 -0
- package/examples/synth.deva +11 -1
- package/examples/synth_types.deva +17 -0
- package/out-tsc/bin/project-version.json +6 -0
- package/out-tsc/core/functions/index.d.ts +5 -0
- package/out-tsc/core/functions/index.js +11 -0
- package/out-tsc/pkg/devalang_core.d.ts +2 -0
- package/out-tsc/pkg/devalang_core.js +17 -2
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +1 -0
- 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} +34 -43
- package/rust/cli/build/commands.rs +153 -103
- package/rust/cli/build/mod.rs +2 -2
- package/rust/cli/build/process.rs +165 -146
- package/rust/cli/check/mod.rs +208 -208
- package/rust/cli/discover/commands.rs +53 -31
- package/rust/cli/discover/config.rs +2 -4
- package/rust/cli/discover/install.rs +139 -28
- package/rust/cli/discover/metadata.rs +3 -3
- package/rust/cli/login/commands.rs +124 -124
- package/rust/cli/me/commands.rs +52 -0
- package/rust/cli/me/mod.rs +1 -0
- package/rust/cli/mod.rs +2 -2
- package/rust/cli/parser.rs +76 -70
- package/rust/cli/play/commands.rs +375 -324
- package/rust/cli/play/mod.rs +5 -5
- package/rust/cli/play/process.rs +159 -150
- package/rust/cli/play/realtime.rs +91 -91
- package/rust/cli/telemetry/commands.rs +22 -22
- package/rust/cli/telemetry/event_creator.rs +80 -80
- package/rust/cli/telemetry/mod.rs +3 -3
- package/rust/cli/telemetry/send.rs +51 -51
- package/rust/cli/template/commands.rs +69 -69
- package/rust/config/driver.rs +112 -103
- package/rust/config/mod.rs +3 -3
- package/rust/config/ops.rs +26 -26
- package/rust/config/settings.rs +101 -101
- package/rust/core/audio/engine/driver.rs +237 -0
- package/rust/core/audio/engine/export.rs +169 -0
- package/rust/core/audio/engine/helpers.rs +178 -170
- package/rust/core/audio/engine/mod.rs +56 -7
- package/rust/core/audio/engine/notes/dsp.rs +88 -0
- package/rust/core/audio/engine/notes/mod.rs +53 -0
- package/rust/core/audio/engine/notes/params.rs +294 -0
- package/rust/core/audio/engine/sample/insert.rs +300 -0
- package/rust/core/audio/engine/sample/mod.rs +40 -0
- package/rust/core/audio/engine/sample/padding.rs +170 -0
- package/rust/core/audio/evaluator/condition.rs +61 -0
- package/rust/core/audio/evaluator/mod.rs +9 -0
- package/rust/core/audio/{evaluator.rs → evaluator/numeric.rs} +152 -310
- package/rust/core/audio/evaluator/rhs.rs +16 -0
- package/rust/core/audio/evaluator/string_expr.rs +94 -0
- package/rust/core/audio/interpreter/driver.rs +574 -542
- package/rust/core/audio/interpreter/mod.rs +2 -14
- package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +179 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +3 -0
- package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +371 -0
- package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -0
- package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -0
- package/rust/core/audio/interpreter/{automate.rs → statements/automate.rs} +2 -4
- package/rust/core/audio/interpreter/{call.rs → statements/call.rs} +36 -5
- package/rust/core/audio/interpreter/{condition.rs → statements/condition.rs} +72 -71
- package/rust/core/audio/interpreter/{function.rs → statements/function.rs} +24 -26
- package/rust/core/audio/interpreter/{let_.rs → statements/let_.rs} +36 -38
- package/rust/core/audio/interpreter/{load.rs → statements/load.rs} +17 -19
- package/rust/core/audio/interpreter/{loop_.rs → statements/loop_.rs} +115 -114
- package/rust/core/audio/interpreter/statements/mod.rs +12 -0
- package/rust/core/audio/interpreter/{sleep.rs → statements/sleep.rs} +28 -28
- package/rust/core/audio/interpreter/{spawn.rs → statements/spawn.rs} +54 -4
- package/rust/core/audio/interpreter/{tempo.rs → statements/tempo.rs} +40 -40
- package/rust/core/audio/interpreter/{trigger.rs → statements/trigger.rs} +242 -239
- package/rust/core/audio/loader/trigger.rs +98 -97
- package/rust/core/audio/mod.rs +6 -7
- package/rust/core/audio/special/easing.rs +189 -189
- package/rust/core/audio/special/env.rs +45 -45
- package/rust/core/audio/special/math.rs +134 -134
- package/rust/core/audio/special/modulator.rs +143 -143
- package/rust/core/builder/mod.rs +129 -86
- package/rust/core/debugger/{module.rs → logs.rs} +52 -55
- package/rust/core/debugger/mod.rs +30 -30
- package/rust/core/debugger/store.rs +38 -40
- package/rust/core/error/mod.rs +269 -269
- package/rust/core/lexer/driver.rs +2 -4
- package/rust/core/mod.rs +9 -10
- package/rust/core/parser/driver/block.rs +111 -0
- package/rust/core/parser/driver/cursor.rs +82 -0
- package/rust/core/parser/driver/driver_impl.rs +159 -0
- package/rust/core/parser/driver/mod.rs +6 -0
- package/rust/core/parser/driver/parse_array.rs +120 -0
- package/rust/core/parser/driver/parse_map.rs +247 -0
- package/rust/core/parser/driver/parser.rs +160 -0
- package/rust/core/parser/handler/arrow_call.rs +90 -15
- package/rust/core/parser/handler/at.rs +279 -279
- package/rust/core/parser/handler/bank.rs +104 -104
- package/rust/core/parser/handler/condition.rs +83 -83
- package/rust/core/parser/handler/dot.rs +148 -148
- package/rust/core/parser/handler/identifier/automate.rs +254 -254
- package/rust/core/parser/handler/identifier/call.rs +91 -91
- package/rust/core/parser/handler/identifier/emit.rs +70 -70
- package/rust/core/parser/handler/identifier/function.rs +113 -113
- package/rust/core/parser/handler/identifier/group.rs +89 -89
- package/rust/core/parser/handler/identifier/let_.rs +173 -173
- package/rust/core/parser/handler/identifier/mod.rs +55 -55
- package/rust/core/parser/handler/identifier/on.rs +107 -107
- package/rust/core/parser/handler/identifier/print.rs +49 -49
- package/rust/core/parser/handler/identifier/sleep.rs +96 -43
- package/rust/core/parser/handler/identifier/spawn.rs +91 -91
- package/rust/core/parser/handler/identifier/synth.rs +39 -3
- package/rust/core/parser/handler/loop_.rs +194 -194
- package/rust/core/parser/handler/pattern.rs +25 -2
- package/rust/core/parser/handler/tempo.rs +105 -57
- package/rust/core/parser/statement.rs +10 -11
- package/rust/core/plugin/loader.rs +137 -137
- package/rust/core/plugin/runner/mod.rs +11 -0
- package/rust/core/plugin/{runner.rs → runner/non_wasm.rs} +206 -72
- package/rust/core/plugin/runner/wasm32.rs +44 -0
- package/rust/core/preprocessor/loader/inject.rs +313 -0
- package/rust/core/preprocessor/loader/loader_helpers.rs +110 -0
- package/rust/core/preprocessor/loader/mod.rs +235 -0
- package/rust/core/preprocessor/module.rs +55 -60
- package/rust/core/preprocessor/{processor.rs → processor/handlers.rs} +107 -114
- package/rust/core/preprocessor/processor/mod.rs +1 -0
- package/rust/core/preprocessor/resolver/function.rs +69 -69
- package/rust/core/preprocessor/resolver/group.rs +122 -94
- package/rust/core/preprocessor/resolver/pattern.rs +14 -2
- package/rust/core/store/global.rs +57 -61
- package/rust/core/store/mod.rs +1 -5
- package/rust/lib.rs +323 -308
- package/rust/macros/Cargo.toml +14 -0
- package/rust/macros/src/lib.rs +52 -0
- package/rust/main.rs +336 -143
- package/rust/types/Cargo.toml +1 -1
- package/rust/types/src/addons.rs +57 -55
- package/rust/types/src/config.rs +82 -74
- package/rust/types/src/lib.rs +15 -12
- package/rust/types/src/plugin.rs +20 -0
- package/rust/types/src/store.rs +139 -0
- package/rust/types/src/telemetry.rs +85 -85
- package/rust/utils/Cargo.toml +5 -2
- package/rust/utils/src/file.rs +477 -94
- package/rust/utils/src/first_usage.rs +97 -97
- package/rust/utils/src/lib.rs +9 -9
- package/rust/utils/src/logger.rs +200 -200
- package/rust/utils/src/path.rs +158 -88
- package/rust/utils/src/signature.rs +41 -41
- package/rust/utils/src/spinner.rs +20 -20
- package/rust/utils/src/version.rs +58 -27
- package/rust/utils/src/watcher.rs +46 -46
- package/rust/web/api.rs +5 -5
- package/rust/web/auth.rs +5 -0
- package/rust/web/cdn.rs +34 -34
- package/rust/web/forge.rs +5 -0
- package/rust/web/mod.rs +2 -0
- package/tests/integration.rs +21 -21
- package/typescript/core/functions/index.ts +11 -0
- package/typescript/pkg/devalang_core.ts +20 -4
- 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 -275
- package/rust/cli/bank/mod.rs +0 -29
- package/rust/cli/install/bank.rs +0 -53
- package/rust/cli/install/commands.rs +0 -35
- package/rust/cli/install/mod.rs +0 -4
- package/rust/cli/install/plugin.rs +0 -61
- package/rust/core/audio/engine/sample.rs +0 -366
- package/rust/core/audio/engine/synth.rs +0 -325
- package/rust/core/audio/interpreter/arrow_call.rs +0 -311
- package/rust/core/audio/renderer.rs +0 -54
- package/rust/core/parser/driver.rs +0 -584
- package/rust/core/preprocessor/loader.rs +0 -637
- package/rust/core/store/export.rs +0 -28
- package/rust/core/store/function.rs +0 -40
- package/rust/core/store/import.rs +0 -28
- package/rust/core/store/variable.rs +0 -51
- package/rust/core/utils/mod.rs +0 -1
- package/rust/core/utils/path.rs +0 -37
|
@@ -1,170 +1,178 @@
|
|
|
1
|
-
use devalang_types::Value;
|
|
2
|
-
use std::collections::HashMap;
|
|
3
|
-
|
|
4
|
-
type OptMap = Option<HashMap<String, Value>>;
|
|
5
|
-
|
|
6
|
-
pub fn env_maps_from_automation(
|
|
7
|
-
automation: &Option<HashMap<String, Value>>,
|
|
8
|
-
) -> (OptMap, OptMap, OptMap) {
|
|
9
|
-
if let Some(auto) = automation {
|
|
10
|
-
let vol = match auto.get("volume") {
|
|
11
|
-
Some(Value::Map(m)) => Some(m.clone()),
|
|
12
|
-
_ => None,
|
|
13
|
-
};
|
|
14
|
-
let pan = match auto.get("pan") {
|
|
15
|
-
Some(Value::Map(m)) => Some(m.clone()),
|
|
16
|
-
_ => None,
|
|
17
|
-
};
|
|
18
|
-
let pit = match auto.get("pitch") {
|
|
19
|
-
Some(Value::Map(m)) => Some(m.clone()),
|
|
20
|
-
_ => None,
|
|
21
|
-
};
|
|
22
|
-
(vol, pan, pit)
|
|
23
|
-
} else {
|
|
24
|
-
(None, None, None)
|
|
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
|
-
if
|
|
66
|
-
return
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
let k = i -
|
|
123
|
-
let denom = if
|
|
124
|
-
(
|
|
125
|
-
} else {
|
|
126
|
-
1.0
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
1
|
+
use devalang_types::Value;
|
|
2
|
+
use std::collections::HashMap;
|
|
3
|
+
|
|
4
|
+
type OptMap = Option<HashMap<String, Value>>;
|
|
5
|
+
|
|
6
|
+
pub fn env_maps_from_automation(
|
|
7
|
+
automation: &Option<HashMap<String, Value>>,
|
|
8
|
+
) -> (OptMap, OptMap, OptMap) {
|
|
9
|
+
if let Some(auto) = automation {
|
|
10
|
+
let vol = match auto.get("volume") {
|
|
11
|
+
Some(Value::Map(m)) => Some(m.clone()),
|
|
12
|
+
_ => None,
|
|
13
|
+
};
|
|
14
|
+
let pan = match auto.get("pan") {
|
|
15
|
+
Some(Value::Map(m)) => Some(m.clone()),
|
|
16
|
+
_ => None,
|
|
17
|
+
};
|
|
18
|
+
let pit = match auto.get("pitch") {
|
|
19
|
+
Some(Value::Map(m)) => Some(m.clone()),
|
|
20
|
+
_ => None,
|
|
21
|
+
};
|
|
22
|
+
(vol, pan, pit)
|
|
23
|
+
} else {
|
|
24
|
+
(None, None, None)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Convert an Option<HashMap<..>> into a HashMap (empty if None).
|
|
29
|
+
pub fn env_map_to_hash(opt: &Option<HashMap<String, Value>>) -> HashMap<String, Value> {
|
|
30
|
+
match opt {
|
|
31
|
+
Some(m) => m.clone(),
|
|
32
|
+
None => HashMap::new(),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub fn eval_env_map(
|
|
37
|
+
env_opt: &Option<HashMap<String, Value>>,
|
|
38
|
+
progress: f32,
|
|
39
|
+
default_val: f32,
|
|
40
|
+
) -> f32 {
|
|
41
|
+
let env = match env_opt {
|
|
42
|
+
Some(m) => m,
|
|
43
|
+
None => {
|
|
44
|
+
return default_val;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
let mut points: Vec<(f32, f32)> = Vec::with_capacity(env.len());
|
|
48
|
+
for (k, v) in env.iter() {
|
|
49
|
+
let key = if k.ends_with('%') {
|
|
50
|
+
&k[..k.len() - 1]
|
|
51
|
+
} else {
|
|
52
|
+
&k[..]
|
|
53
|
+
};
|
|
54
|
+
if let Ok(mut p) = key.parse::<f32>() {
|
|
55
|
+
p = (p / 100.0).clamp(0.0, 1.0);
|
|
56
|
+
let val = match v {
|
|
57
|
+
Value::Number(n) => *n,
|
|
58
|
+
Value::String(s) => s.parse::<f32>().unwrap_or(default_val),
|
|
59
|
+
Value::Identifier(s) => s.parse::<f32>().unwrap_or(default_val),
|
|
60
|
+
_ => default_val,
|
|
61
|
+
};
|
|
62
|
+
points.push((p, val));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if points.is_empty() {
|
|
66
|
+
return default_val;
|
|
67
|
+
}
|
|
68
|
+
points.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
|
|
69
|
+
let t = progress.clamp(0.0, 1.0);
|
|
70
|
+
if t <= points[0].0 {
|
|
71
|
+
return points[0].1;
|
|
72
|
+
}
|
|
73
|
+
if t >= points[points.len() - 1].0 {
|
|
74
|
+
return points[points.len() - 1].1;
|
|
75
|
+
}
|
|
76
|
+
for w in points.windows(2) {
|
|
77
|
+
let (p0, v0) = w[0];
|
|
78
|
+
let (p1, v1) = w[1];
|
|
79
|
+
if t >= p0 && t <= p1 {
|
|
80
|
+
let ratio = if (p1 - p0).abs() < f32::EPSILON {
|
|
81
|
+
0.0
|
|
82
|
+
} else {
|
|
83
|
+
(t - p0) / (p1 - p0)
|
|
84
|
+
};
|
|
85
|
+
return v0 + (v1 - v0) * ratio;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
default_val
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub fn oscillator_sample(waveform: &str, current_freq: f32, t: f32) -> f32 {
|
|
92
|
+
let phase = 2.0 * std::f32::consts::PI * current_freq * t;
|
|
93
|
+
match waveform {
|
|
94
|
+
"sine" => phase.sin(),
|
|
95
|
+
"square" => {
|
|
96
|
+
if phase.sin() >= 0.0 {
|
|
97
|
+
1.0
|
|
98
|
+
} else {
|
|
99
|
+
-1.0
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
"saw" => 2.0 * (current_freq * t - (current_freq * t + 0.5).floor()),
|
|
103
|
+
"triangle" => (2.0 * (2.0 * (current_freq * t).fract() - 1.0)).abs() * 2.0 - 1.0,
|
|
104
|
+
_ => 0.0,
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pub fn adsr_envelope_value(
|
|
109
|
+
i: usize,
|
|
110
|
+
attack_samples: usize,
|
|
111
|
+
decay_samples: usize,
|
|
112
|
+
sustain_samples: usize,
|
|
113
|
+
release_samples: usize,
|
|
114
|
+
sustain_level: f32,
|
|
115
|
+
) -> f32 {
|
|
116
|
+
let attack_start = 0usize;
|
|
117
|
+
let decay_start = attack_samples;
|
|
118
|
+
let sustain_start = attack_samples + decay_samples;
|
|
119
|
+
let release_start = attack_samples + decay_samples + sustain_samples;
|
|
120
|
+
|
|
121
|
+
if i < attack_start + attack_samples && attack_samples > 0 {
|
|
122
|
+
let k = i - attack_start;
|
|
123
|
+
let denom = if attack_samples > 1 {
|
|
124
|
+
(attack_samples - 1) as f32
|
|
125
|
+
} else {
|
|
126
|
+
1.0
|
|
127
|
+
};
|
|
128
|
+
(k as f32) / denom
|
|
129
|
+
} else if i < decay_start + decay_samples && decay_samples > 0 {
|
|
130
|
+
let k = i - decay_start;
|
|
131
|
+
let denom = if decay_samples > 1 {
|
|
132
|
+
(decay_samples - 1) as f32
|
|
133
|
+
} else {
|
|
134
|
+
1.0
|
|
135
|
+
};
|
|
136
|
+
let ratio = (k as f32) / denom;
|
|
137
|
+
1.0 - (1.0 - sustain_level) * ratio
|
|
138
|
+
} else if i < sustain_start + sustain_samples {
|
|
139
|
+
sustain_level
|
|
140
|
+
} else if release_samples > 0 {
|
|
141
|
+
// release: interpolate from sustain_level down to 0 inclusive
|
|
142
|
+
let k = i.saturating_sub(release_start);
|
|
143
|
+
let denom = if release_samples > 1 {
|
|
144
|
+
(release_samples - 1) as f32
|
|
145
|
+
} else {
|
|
146
|
+
1.0
|
|
147
|
+
};
|
|
148
|
+
let ratio = (k as f32) / denom;
|
|
149
|
+
let val = sustain_level * (1.0 - ratio);
|
|
150
|
+
if val < 0.0 { 0.0 } else { val }
|
|
151
|
+
} else {
|
|
152
|
+
0.0
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
pub fn pan_gains(pan_val: f32) -> (f32, f32) {
|
|
157
|
+
let left_gain = 1.0 - pan_val.max(0.0);
|
|
158
|
+
let right_gain = 1.0 + pan_val.min(0.0);
|
|
159
|
+
(left_gain, right_gain)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
pub fn mix_stereo_samples_into_buffer(
|
|
163
|
+
engine: &mut super::driver::AudioEngine,
|
|
164
|
+
start_sample: usize,
|
|
165
|
+
channels: usize,
|
|
166
|
+
stereo_samples: &[i16],
|
|
167
|
+
) {
|
|
168
|
+
let offset = start_sample * channels;
|
|
169
|
+
let required_len = offset + stereo_samples.len();
|
|
170
|
+
|
|
171
|
+
if engine.buffer.len() < required_len {
|
|
172
|
+
engine.buffer.resize(required_len, 0);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (i, sample) in stereo_samples.iter().enumerate() {
|
|
176
|
+
engine.buffer[offset + i] = engine.buffer[offset + i].saturating_add(*sample);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -1,7 +1,56 @@
|
|
|
1
|
-
pub mod
|
|
2
|
-
pub mod
|
|
3
|
-
pub mod
|
|
4
|
-
|
|
5
|
-
pub
|
|
6
|
-
|
|
7
|
-
pub use
|
|
1
|
+
pub mod driver;
|
|
2
|
+
pub mod export;
|
|
3
|
+
pub mod helpers;
|
|
4
|
+
pub mod notes;
|
|
5
|
+
pub mod sample;
|
|
6
|
+
|
|
7
|
+
pub use driver::AudioEngine;
|
|
8
|
+
|
|
9
|
+
pub use driver::CHANNELS;
|
|
10
|
+
pub use driver::MidiNoteEvent;
|
|
11
|
+
pub use driver::SAMPLE_RATE;
|
|
12
|
+
pub use helpers::*;
|
|
13
|
+
|
|
14
|
+
use crate::core::audio::interpreter::driver::run_audio_program;
|
|
15
|
+
use crate::core::parser::statement::Statement;
|
|
16
|
+
use crate::core::store::global::GlobalStore;
|
|
17
|
+
use devalang_types::{FunctionTable, VariableTable};
|
|
18
|
+
use std::collections::HashMap;
|
|
19
|
+
|
|
20
|
+
/// Render audio for a set of parsed modules.
|
|
21
|
+
///
|
|
22
|
+
/// For each entry in `modules` (module path -> statements), create an
|
|
23
|
+
/// AudioEngine, setup empty module-local VariableTable and FunctionTable,
|
|
24
|
+
/// invoke the interpreter driver to schedule audio into the engine, and
|
|
25
|
+
/// return a map of module name -> AudioEngine.
|
|
26
|
+
pub fn render_audio_with_modules(
|
|
27
|
+
modules: HashMap<String, Vec<Statement>>,
|
|
28
|
+
_output_dir: &str,
|
|
29
|
+
global_store: &mut GlobalStore,
|
|
30
|
+
) -> HashMap<String, AudioEngine> {
|
|
31
|
+
let mut result: HashMap<String, AudioEngine> = HashMap::new();
|
|
32
|
+
|
|
33
|
+
for (module_name, statements) in modules.into_iter() {
|
|
34
|
+
// Create engine named after module (used for diagnostics)
|
|
35
|
+
let mut engine = AudioEngine::new(module_name.clone());
|
|
36
|
+
|
|
37
|
+
// Create empty module-local tables; interpreter expects these as starting context
|
|
38
|
+
let module_vars = VariableTable::new();
|
|
39
|
+
let module_funcs = FunctionTable::new();
|
|
40
|
+
|
|
41
|
+
// Run interpreter which will populate the engine.buffer and midi events
|
|
42
|
+
let (_max_end_time, _cursor_time) = run_audio_program(
|
|
43
|
+
&statements,
|
|
44
|
+
&mut engine,
|
|
45
|
+
module_name.clone(),
|
|
46
|
+
module_name.clone(),
|
|
47
|
+
module_vars,
|
|
48
|
+
module_funcs,
|
|
49
|
+
global_store,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
result.insert(module_name, engine);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
result
|
|
56
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
use crate::core::audio::engine::notes::params::NoteSetup;
|
|
2
|
+
use devalang_types::Value;
|
|
3
|
+
use std::collections::HashMap;
|
|
4
|
+
|
|
5
|
+
pub fn render_notes_into_buffer(
|
|
6
|
+
engine: &mut crate::core::audio::engine::AudioEngine,
|
|
7
|
+
_waveform: &str,
|
|
8
|
+
_freq: f32,
|
|
9
|
+
_amp: f32,
|
|
10
|
+
_start_time_ms: f32,
|
|
11
|
+
_duration_ms: f32,
|
|
12
|
+
_synth_params: HashMap<String, Value>,
|
|
13
|
+
_note_params: HashMap<String, Value>,
|
|
14
|
+
_automation: Option<HashMap<String, Value>>,
|
|
15
|
+
setup: NoteSetup,
|
|
16
|
+
) -> Vec<(usize, usize)> {
|
|
17
|
+
use crate::core::audio::engine::helpers;
|
|
18
|
+
|
|
19
|
+
let sample_rate = setup.sample_rate;
|
|
20
|
+
let channels = setup.channels;
|
|
21
|
+
let total_samples = setup.total_samples;
|
|
22
|
+
let start_sample = setup.start_sample;
|
|
23
|
+
|
|
24
|
+
let mut stereo_samples: Vec<i16> = Vec::with_capacity(total_samples * channels);
|
|
25
|
+
let fade_len = (sample_rate * 0.01) as usize; // 10ms fade
|
|
26
|
+
|
|
27
|
+
for i in 0..total_samples {
|
|
28
|
+
let t = ((start_sample + i) as f32) / sample_rate;
|
|
29
|
+
|
|
30
|
+
// simplified voice/unison and oscillator sampling
|
|
31
|
+
let mut value = helpers::oscillator_sample(_waveform, _freq, t);
|
|
32
|
+
|
|
33
|
+
// apply ADSR envelope
|
|
34
|
+
let envelope = helpers::adsr_envelope_value(
|
|
35
|
+
i,
|
|
36
|
+
setup.attack_samples,
|
|
37
|
+
setup.decay_samples,
|
|
38
|
+
if total_samples > setup.attack_samples + setup.decay_samples + setup.release_samples {
|
|
39
|
+
total_samples - setup.attack_samples - setup.decay_samples - setup.release_samples
|
|
40
|
+
} else {
|
|
41
|
+
0
|
|
42
|
+
},
|
|
43
|
+
setup.release_samples,
|
|
44
|
+
setup.sustain_level,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
value *= envelope * (i16::MAX as f32) * _amp;
|
|
48
|
+
|
|
49
|
+
if fade_len > 0 && i < fade_len {
|
|
50
|
+
if fade_len == 1 {
|
|
51
|
+
value *= 0.0;
|
|
52
|
+
} else {
|
|
53
|
+
value *= (i as f32) / (fade_len as f32);
|
|
54
|
+
}
|
|
55
|
+
} else if fade_len > 0 && i >= total_samples.saturating_sub(fade_len) {
|
|
56
|
+
if fade_len == 1 {
|
|
57
|
+
value *= 0.0;
|
|
58
|
+
} else {
|
|
59
|
+
value *= ((total_samples - 1 - i) as f32) / ((fade_len - 1) as f32);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let (left_gain, right_gain) = helpers::pan_gains(0.0);
|
|
64
|
+
let left = (value * left_gain)
|
|
65
|
+
.round()
|
|
66
|
+
.clamp(i16::MIN as f32, i16::MAX as f32) as i16;
|
|
67
|
+
let right = (value * right_gain)
|
|
68
|
+
.round()
|
|
69
|
+
.clamp(i16::MIN as f32, i16::MAX as f32) as i16;
|
|
70
|
+
|
|
71
|
+
// push samples interleaved for channels=2, fallback zeros for other channel counts
|
|
72
|
+
if channels >= 2 {
|
|
73
|
+
stereo_samples.push(left);
|
|
74
|
+
stereo_samples.push(right);
|
|
75
|
+
} else if channels == 1 {
|
|
76
|
+
stereo_samples.push(((left as i32 + right as i32) / 2) as i16);
|
|
77
|
+
} else {
|
|
78
|
+
stereo_samples.push(left);
|
|
79
|
+
stereo_samples.push(right);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
engine.note_count = engine.note_count.saturating_add(1);
|
|
84
|
+
helpers::mix_stereo_samples_into_buffer(engine, start_sample, channels, &stereo_samples);
|
|
85
|
+
|
|
86
|
+
// Return the inserted sample range for this note (start_sample, total_samples)
|
|
87
|
+
vec![(start_sample, stereo_samples.len())]
|
|
88
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
use devalang_types::Value;
|
|
2
|
+
use std::collections::HashMap;
|
|
3
|
+
|
|
4
|
+
pub mod dsp;
|
|
5
|
+
pub mod params;
|
|
6
|
+
|
|
7
|
+
// Minimal safe facade: parse the setup then call DSP renderer.
|
|
8
|
+
|
|
9
|
+
pub fn insert_note_impl(
|
|
10
|
+
engine: &mut crate::core::audio::engine::AudioEngine,
|
|
11
|
+
owner: Option<String>,
|
|
12
|
+
waveform: String,
|
|
13
|
+
freq: f32,
|
|
14
|
+
amp: f32,
|
|
15
|
+
start_time_ms: f32,
|
|
16
|
+
duration_ms: f32,
|
|
17
|
+
synth_params: HashMap<String, Value>,
|
|
18
|
+
note_params: HashMap<String, Value>,
|
|
19
|
+
automation: Option<HashMap<String, Value>>,
|
|
20
|
+
) -> Vec<(usize, usize)> {
|
|
21
|
+
let setup = params::build_note_setup(
|
|
22
|
+
engine,
|
|
23
|
+
&waveform,
|
|
24
|
+
freq,
|
|
25
|
+
amp,
|
|
26
|
+
start_time_ms,
|
|
27
|
+
duration_ms,
|
|
28
|
+
&synth_params,
|
|
29
|
+
¬e_params,
|
|
30
|
+
&automation,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
let ranges = dsp::render_notes_into_buffer(
|
|
34
|
+
engine,
|
|
35
|
+
&waveform,
|
|
36
|
+
freq,
|
|
37
|
+
amp,
|
|
38
|
+
start_time_ms,
|
|
39
|
+
duration_ms,
|
|
40
|
+
synth_params,
|
|
41
|
+
note_params,
|
|
42
|
+
automation,
|
|
43
|
+
setup,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if let Some(owner_name) = owner {
|
|
47
|
+
for (start, len) in ranges.iter() {
|
|
48
|
+
engine.record_last_note_range(&owner_name, *start, *len);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
ranges
|
|
53
|
+
}
|