@devaloop/devalang 0.0.1-beta.2 → 0.1.1
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/README.md +177 -146
- package/out-tsc/api.d.ts +180 -0
- package/out-tsc/api.d.ts.map +1 -0
- package/out-tsc/api.js +286 -0
- package/out-tsc/api.js.map +1 -0
- package/out-tsc/bin/index.d.ts +12 -0
- package/out-tsc/bin/index.d.ts.map +1 -0
- package/out-tsc/bin/index.js +20 -54
- package/out-tsc/bin/index.js.map +1 -0
- package/out-tsc/examples/basic-usage.d.ts +8 -0
- package/out-tsc/examples/basic-usage.d.ts.map +1 -0
- package/out-tsc/examples/basic-usage.js +113 -0
- package/out-tsc/examples/basic-usage.js.map +1 -0
- package/out-tsc/index.d.ts +19 -5
- package/out-tsc/index.d.ts.map +1 -0
- package/out-tsc/index.js +24 -6
- package/out-tsc/index.js.map +1 -0
- package/out-tsc/scripts/copy-wasm-dts.d.ts +7 -0
- package/out-tsc/scripts/copy-wasm-dts.d.ts.map +1 -0
- package/out-tsc/scripts/copy-wasm-dts.js +36 -32
- package/out-tsc/scripts/copy-wasm-dts.js.map +1 -0
- package/out-tsc/scripts/postinstall.d.ts +1 -0
- package/out-tsc/scripts/postinstall.d.ts.map +1 -0
- package/out-tsc/scripts/postinstall.js +4 -1
- package/out-tsc/scripts/postinstall.js.map +1 -0
- package/out-tsc/scripts/version/bump.d.ts +5 -1
- package/out-tsc/scripts/version/bump.d.ts.map +1 -0
- package/out-tsc/scripts/version/bump.js +114 -44
- package/out-tsc/scripts/version/bump.js.map +1 -0
- package/out-tsc/scripts/version/fetch.d.ts +12 -1
- package/out-tsc/scripts/version/fetch.d.ts.map +1 -0
- package/out-tsc/scripts/version/fetch.js +68 -24
- package/out-tsc/scripts/version/fetch.js.map +1 -0
- package/out-tsc/scripts/version/index.d.ts +6 -0
- package/out-tsc/scripts/version/index.d.ts.map +1 -0
- package/out-tsc/scripts/version/index.js +44 -22
- package/out-tsc/scripts/version/index.js.map +1 -0
- package/out-tsc/scripts/version/sync.d.ts +5 -1
- package/out-tsc/scripts/version/sync.d.ts.map +1 -0
- package/out-tsc/scripts/version/sync.js +78 -29
- package/out-tsc/scripts/version/sync.js.map +1 -0
- package/out-tsc/types.d.ts +68 -0
- package/out-tsc/types.d.ts.map +1 -0
- package/out-tsc/{core/types/value.js → types.js} +4 -0
- package/out-tsc/types.js.map +1 -0
- package/out-tsc/wasm.d.ts +8 -0
- package/out-tsc/wasm.d.ts.map +1 -0
- package/out-tsc/{core/index.js → wasm.js} +9 -6
- package/out-tsc/wasm.js.map +1 -0
- package/package.json +50 -37
- package/.cargo/config.toml +0 -2
- package/.devalang +0 -9
- package/.github/workflows/ci.yml +0 -103
- package/Cargo.toml +0 -81
- package/docs/CHANGELOG.md +0 -581
- package/docs/CONTRIBUTING.md +0 -101
- package/docs/ROADMAP.md +0 -38
- package/docs/TODO.md +0 -71
- package/examples/automation.deva +0 -42
- package/examples/bank.deva +0 -7
- package/examples/bus.deva +0 -10
- package/examples/condition.deva +0 -20
- package/examples/duration.deva +0 -9
- package/examples/effect.deva +0 -2
- package/examples/events.deva +0 -12
- package/examples/filter.deva +0 -11
- package/examples/function.deva +0 -15
- package/examples/group.deva +0 -12
- package/examples/index.deva +0 -63
- package/examples/lfo.deva +0 -9
- package/examples/loop.deva +0 -10
- package/examples/pattern.deva +0 -8
- package/examples/plugin.deva +0 -16
- package/examples/samples/hat-808.wav +0 -0
- package/examples/samples/kick-808.wav +0 -0
- package/examples/synth.deva +0 -24
- package/examples/synth_types.deva +0 -17
- package/examples/variables.deva +0 -9
- package/out-tsc/core/functions/index.d.ts +0 -42
- package/out-tsc/core/functions/index.js +0 -87
- package/out-tsc/core/index.d.ts +0 -6
- package/out-tsc/core/types/index.d.ts +0 -4
- package/out-tsc/core/types/index.js +0 -20
- package/out-tsc/core/types/plugin.d.ts +0 -18
- package/out-tsc/core/types/plugin.js +0 -2
- package/out-tsc/core/types/result.d.ts +0 -27
- package/out-tsc/core/types/result.js +0 -2
- package/out-tsc/core/types/statement.d.ts +0 -106
- package/out-tsc/core/types/statement.js +0 -2
- package/out-tsc/core/types/value.d.ts +0 -43
- package/out-tsc/pkg/devalang_core.d.ts +0 -15
- package/out-tsc/pkg/devalang_core.js +0 -65
- package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +0 -34
- package/project-version.json +0 -6
- 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/build/commands.rs +0 -153
- package/rust/cli/build/mod.rs +0 -2
- package/rust/cli/build/process.rs +0 -165
- package/rust/cli/check/mod.rs +0 -208
- package/rust/cli/discover/commands.rs +0 -253
- package/rust/cli/discover/config.rs +0 -111
- package/rust/cli/discover/fs.rs +0 -19
- package/rust/cli/discover/install.rs +0 -103
- package/rust/cli/discover/metadata.rs +0 -48
- package/rust/cli/discover/mod.rs +0 -5
- package/rust/cli/init/commands.rs +0 -88
- package/rust/cli/init/mod.rs +0 -1
- package/rust/cli/install/addon.rs +0 -118
- 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
- package/rust/cli/login/commands.rs +0 -124
- package/rust/cli/login/mod.rs +0 -1
- package/rust/cli/mod.rs +0 -12
- package/rust/cli/parser.rs +0 -359
- package/rust/cli/play/commands.rs +0 -375
- package/rust/cli/play/io.rs +0 -17
- package/rust/cli/play/mod.rs +0 -5
- package/rust/cli/play/process.rs +0 -159
- package/rust/cli/play/realtime.rs +0 -91
- package/rust/cli/play/utils.rs +0 -23
- package/rust/cli/telemetry/commands.rs +0 -22
- package/rust/cli/telemetry/event_creator.rs +0 -80
- package/rust/cli/telemetry/mod.rs +0 -3
- package/rust/cli/telemetry/send.rs +0 -51
- package/rust/cli/template/commands.rs +0 -69
- package/rust/cli/template/mod.rs +0 -1
- package/rust/cli/update/commands.rs +0 -6
- package/rust/cli/update/mod.rs +0 -1
- package/rust/config/driver.rs +0 -112
- package/rust/config/mod.rs +0 -3
- package/rust/config/ops.rs +0 -26
- package/rust/config/settings.rs +0 -101
- package/rust/core/audio/engine/driver.rs +0 -220
- package/rust/core/audio/engine/export.rs +0 -169
- package/rust/core/audio/engine/helpers.rs +0 -178
- package/rust/core/audio/engine/mod.rs +0 -56
- package/rust/core/audio/engine/notes/dsp.rs +0 -85
- package/rust/core/audio/engine/notes/mod.rs +0 -44
- package/rust/core/audio/engine/notes/params.rs +0 -294
- package/rust/core/audio/engine/sample/insert.rs +0 -199
- package/rust/core/audio/engine/sample/mod.rs +0 -40
- package/rust/core/audio/engine/sample/padding.rs +0 -170
- package/rust/core/audio/evaluator/condition.rs +0 -61
- package/rust/core/audio/evaluator/mod.rs +0 -9
- package/rust/core/audio/evaluator/numeric.rs +0 -152
- package/rust/core/audio/evaluator/rhs.rs +0 -16
- package/rust/core/audio/evaluator/string_expr.rs +0 -94
- package/rust/core/audio/interpreter/driver.rs +0 -574
- package/rust/core/audio/interpreter/mod.rs +0 -2
- package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +0 -175
- package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +0 -384
- package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +0 -2
- package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +0 -316
- package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +0 -3
- package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +0 -192
- package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +0 -24
- package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +0 -116
- package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +0 -97
- package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +0 -100
- package/rust/core/audio/interpreter/statements/automate.rs +0 -16
- package/rust/core/audio/interpreter/statements/call.rs +0 -295
- package/rust/core/audio/interpreter/statements/condition.rs +0 -72
- package/rust/core/audio/interpreter/statements/function.rs +0 -24
- package/rust/core/audio/interpreter/statements/let_.rs +0 -36
- package/rust/core/audio/interpreter/statements/load.rs +0 -17
- package/rust/core/audio/interpreter/statements/loop_.rs +0 -115
- package/rust/core/audio/interpreter/statements/mod.rs +0 -12
- package/rust/core/audio/interpreter/statements/sleep.rs +0 -28
- package/rust/core/audio/interpreter/statements/spawn.rs +0 -253
- package/rust/core/audio/interpreter/statements/tempo.rs +0 -40
- package/rust/core/audio/interpreter/statements/trigger.rs +0 -239
- package/rust/core/audio/loader/mod.rs +0 -1
- package/rust/core/audio/loader/trigger.rs +0 -98
- package/rust/core/audio/mod.rs +0 -6
- package/rust/core/audio/player.rs +0 -70
- package/rust/core/audio/special/easing.rs +0 -189
- package/rust/core/audio/special/env.rs +0 -45
- package/rust/core/audio/special/math.rs +0 -134
- package/rust/core/audio/special/mod.rs +0 -9
- package/rust/core/audio/special/modulator.rs +0 -143
- package/rust/core/builder/mod.rs +0 -129
- package/rust/core/debugger/lexer.rs +0 -27
- package/rust/core/debugger/logs.rs +0 -52
- package/rust/core/debugger/mod.rs +0 -30
- package/rust/core/debugger/preprocessor.rs +0 -27
- package/rust/core/debugger/store.rs +0 -38
- package/rust/core/error/mod.rs +0 -269
- package/rust/core/lexer/driver.rs +0 -59
- package/rust/core/lexer/handler/arrow.rs +0 -82
- package/rust/core/lexer/handler/at.rs +0 -21
- package/rust/core/lexer/handler/brace.rs +0 -41
- package/rust/core/lexer/handler/colon.rs +0 -21
- package/rust/core/lexer/handler/comment.rs +0 -30
- package/rust/core/lexer/handler/dot.rs +0 -21
- package/rust/core/lexer/handler/driver.rs +0 -337
- package/rust/core/lexer/handler/identifier.rs +0 -47
- package/rust/core/lexer/handler/indent.rs +0 -66
- package/rust/core/lexer/handler/mod.rs +0 -15
- package/rust/core/lexer/handler/newline.rs +0 -23
- package/rust/core/lexer/handler/number.rs +0 -31
- package/rust/core/lexer/handler/operator.rs +0 -46
- package/rust/core/lexer/handler/parenthesis.rs +0 -41
- package/rust/core/lexer/handler/slash.rs +0 -21
- package/rust/core/lexer/handler/string.rs +0 -63
- package/rust/core/lexer/mod.rs +0 -3
- package/rust/core/lexer/token.rs +0 -91
- package/rust/core/mod.rs +0 -9
- package/rust/core/parser/driver/block.rs +0 -111
- package/rust/core/parser/driver/cursor.rs +0 -82
- package/rust/core/parser/driver/driver_impl.rs +0 -139
- package/rust/core/parser/driver/mod.rs +0 -6
- package/rust/core/parser/driver/parse_array.rs +0 -120
- package/rust/core/parser/driver/parse_map.rs +0 -223
- package/rust/core/parser/driver/parser.rs +0 -160
- package/rust/core/parser/handler/arrow_call.rs +0 -277
- package/rust/core/parser/handler/at.rs +0 -279
- package/rust/core/parser/handler/bank.rs +0 -104
- package/rust/core/parser/handler/condition.rs +0 -83
- package/rust/core/parser/handler/dot.rs +0 -148
- package/rust/core/parser/handler/identifier/automate.rs +0 -254
- package/rust/core/parser/handler/identifier/call.rs +0 -91
- package/rust/core/parser/handler/identifier/emit.rs +0 -70
- package/rust/core/parser/handler/identifier/function.rs +0 -113
- package/rust/core/parser/handler/identifier/group.rs +0 -89
- package/rust/core/parser/handler/identifier/let_.rs +0 -173
- package/rust/core/parser/handler/identifier/mod.rs +0 -55
- package/rust/core/parser/handler/identifier/on.rs +0 -107
- package/rust/core/parser/handler/identifier/print.rs +0 -49
- package/rust/core/parser/handler/identifier/sleep.rs +0 -96
- package/rust/core/parser/handler/identifier/spawn.rs +0 -91
- package/rust/core/parser/handler/identifier/synth.rs +0 -135
- package/rust/core/parser/handler/loop_.rs +0 -194
- package/rust/core/parser/handler/mod.rs +0 -9
- package/rust/core/parser/handler/pattern.rs +0 -74
- package/rust/core/parser/handler/tempo.rs +0 -105
- package/rust/core/parser/mod.rs +0 -3
- package/rust/core/parser/statement.rs +0 -10
- package/rust/core/plugin/loader.rs +0 -137
- package/rust/core/plugin/mod.rs +0 -2
- package/rust/core/plugin/runner/mod.rs +0 -11
- package/rust/core/plugin/runner/non_wasm.rs +0 -297
- package/rust/core/plugin/runner/wasm32.rs +0 -43
- package/rust/core/preprocessor/loader/inject.rs +0 -278
- package/rust/core/preprocessor/loader/loader_helpers.rs +0 -110
- package/rust/core/preprocessor/loader/mod.rs +0 -235
- package/rust/core/preprocessor/mod.rs +0 -4
- package/rust/core/preprocessor/module.rs +0 -55
- package/rust/core/preprocessor/processor/handlers.rs +0 -107
- package/rust/core/preprocessor/processor/mod.rs +0 -1
- package/rust/core/preprocessor/resolver/bank.rs +0 -49
- package/rust/core/preprocessor/resolver/call.rs +0 -124
- package/rust/core/preprocessor/resolver/condition.rs +0 -95
- package/rust/core/preprocessor/resolver/driver.rs +0 -324
- package/rust/core/preprocessor/resolver/function.rs +0 -69
- package/rust/core/preprocessor/resolver/group.rs +0 -122
- package/rust/core/preprocessor/resolver/let_.rs +0 -32
- package/rust/core/preprocessor/resolver/loop_.rs +0 -318
- package/rust/core/preprocessor/resolver/mod.rs +0 -16
- package/rust/core/preprocessor/resolver/pattern.rs +0 -83
- package/rust/core/preprocessor/resolver/spawn.rs +0 -99
- package/rust/core/preprocessor/resolver/synth.rs +0 -54
- package/rust/core/preprocessor/resolver/tempo.rs +0 -48
- package/rust/core/preprocessor/resolver/trigger.rs +0 -116
- package/rust/core/preprocessor/resolver/value.rs +0 -176
- package/rust/core/store/global.rs +0 -57
- package/rust/core/store/mod.rs +0 -1
- package/rust/lib.rs +0 -323
- package/rust/main.rs +0 -388
- package/rust/types/Cargo.toml +0 -11
- package/rust/types/src/addons.rs +0 -55
- package/rust/types/src/ast.rs +0 -202
- package/rust/types/src/config.rs +0 -84
- package/rust/types/src/lib.rs +0 -15
- package/rust/types/src/plugin.rs +0 -20
- package/rust/types/src/store.rs +0 -139
- package/rust/types/src/telemetry.rs +0 -85
- package/rust/utils/Cargo.toml +0 -26
- package/rust/utils/src/error.rs +0 -186
- package/rust/utils/src/file.rs +0 -94
- package/rust/utils/src/first_usage.rs +0 -97
- package/rust/utils/src/lib.rs +0 -9
- package/rust/utils/src/logger.rs +0 -200
- package/rust/utils/src/path.rs +0 -129
- package/rust/utils/src/signature.rs +0 -41
- package/rust/utils/src/spinner.rs +0 -20
- package/rust/utils/src/version.rs +0 -27
- package/rust/utils/src/watcher.rs +0 -46
- package/rust/web/api.rs +0 -5
- package/rust/web/cdn.rs +0 -34
- package/rust/web/mod.rs +0 -3
- package/rust/web/sso.rs +0 -5
- package/templates/minimal/.devalang +0 -5
- package/templates/minimal/README.md +0 -218
- package/templates/minimal/src/index.deva +0 -2
- package/templates/welcome/.devalang +0 -5
- package/templates/welcome/README.md +0 -218
- package/templates/welcome/samples/kick-808.wav +0 -0
- package/templates/welcome/src/index.deva +0 -61
- package/templates/welcome/src/variables.deva +0 -3
- package/tests/integration.rs +0 -21
- package/tests/rust/cli_check_build.rs +0 -21
- package/tests/rust/cli_help.rs +0 -12
- package/tests/rust/cli_template_list.rs +0 -10
- package/tests/rust/cli_version.rs +0 -11
- package/tests/typescript/index.spec.ts +0 -136
- package/tests/typescript/playhead.spec.ts +0 -36
- package/tests/typescript/render_e2e.spec.ts +0 -77
- package/tsconfig.json +0 -115
- package/typescript/bin/index.ts +0 -28
- package/typescript/core/functions/index.ts +0 -94
- package/typescript/core/index.ts +0 -6
- package/typescript/core/types/index.ts +0 -4
- package/typescript/core/types/plugin.ts +0 -19
- package/typescript/core/types/result.ts +0 -29
- package/typescript/core/types/statement.ts +0 -47
- package/typescript/core/types/value.ts +0 -29
- package/typescript/index.ts +0 -8
- package/typescript/pkg/devalang_core.d.ts +0 -4
- package/typescript/pkg/devalang_core.ts +0 -65
- package/typescript/scripts/copy-wasm-dts.ts +0 -41
- package/typescript/scripts/postinstall.ts +0 -85
- package/typescript/scripts/version/bump.ts +0 -44
- package/typescript/scripts/version/fetch.ts +0 -18
- package/typescript/scripts/version/index.ts +0 -25
- package/typescript/scripts/version/sync.ts +0 -24
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
use crate::core::{audio::engine::AudioEngine, plugin::runner::WasmPluginRunner};
|
|
2
|
-
use devalang_types::Value;
|
|
3
|
-
use devalang_utils::logger::{LogLevel, Logger};
|
|
4
|
-
use std::collections::HashMap;
|
|
5
|
-
|
|
6
|
-
pub fn interprete_note_method(
|
|
7
|
-
args: &Vec<devalang_types::Value>,
|
|
8
|
-
target: &str,
|
|
9
|
-
audio_engine: &mut AudioEngine,
|
|
10
|
-
variable_table: &devalang_types::VariableTable,
|
|
11
|
-
global_store: &crate::core::store::global::GlobalStore,
|
|
12
|
-
waveform_str: &str,
|
|
13
|
-
synth_params: &HashMap<String, devalang_types::Value>,
|
|
14
|
-
amp: f32,
|
|
15
|
-
base_bpm: f32,
|
|
16
|
-
base_duration: f32,
|
|
17
|
-
max_end_time: &mut f32,
|
|
18
|
-
mut cursor_time: Option<&mut f32>,
|
|
19
|
-
cursor_copy: f32,
|
|
20
|
-
update_cursor: bool,
|
|
21
|
-
) -> (f32, f32) {
|
|
22
|
-
let filtered_args: Vec<_> = args
|
|
23
|
-
.iter()
|
|
24
|
-
.filter(|arg| !matches!(arg, Value::Unknown))
|
|
25
|
-
.collect();
|
|
26
|
-
|
|
27
|
-
let Some(Value::Identifier(note_name)) = filtered_args.first().map(|v| (*v).clone()) else {
|
|
28
|
-
println!(
|
|
29
|
-
"❌ Invalid or missing argument for 'note' method on '{}'.",
|
|
30
|
-
target
|
|
31
|
-
);
|
|
32
|
-
return (*max_end_time, cursor_copy);
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
let mut note_params = HashMap::new();
|
|
36
|
-
if let Some(arg1) = filtered_args.get(1) {
|
|
37
|
-
if let Value::Map(map) = (*arg1).clone() {
|
|
38
|
-
for (key, value) in map {
|
|
39
|
-
note_params.insert(key, value);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Note parameters and calculations
|
|
45
|
-
let amp_note = extract_f32(¬e_params, "amp", base_bpm).unwrap_or(amp);
|
|
46
|
-
let duration_ms =
|
|
47
|
-
extract_f32(¬e_params, "duration", base_bpm).unwrap_or(base_duration * 1000.0);
|
|
48
|
-
|
|
49
|
-
let duration_secs = duration_ms / 1000.0;
|
|
50
|
-
let final_freq = note_to_freq(¬e_name);
|
|
51
|
-
let start_time = cursor_copy;
|
|
52
|
-
let end_time = start_time + duration_secs;
|
|
53
|
-
|
|
54
|
-
// Fetch automation map if present:
|
|
55
|
-
// - Global (per-synth): key "<target>__automation" => map with key "params"
|
|
56
|
-
// - Per-note: note parameter "automate" => map
|
|
57
|
-
let auto_key = format!("{}__automation", target);
|
|
58
|
-
let synth_automation = match variable_table.get(&auto_key) {
|
|
59
|
-
Some(Value::Map(map)) => match map.get("params") {
|
|
60
|
-
Some(Value::Map(p)) => Some(p.clone()),
|
|
61
|
-
_ => None,
|
|
62
|
-
},
|
|
63
|
-
_ => None,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
let note_automation = match note_params.get("automate") {
|
|
67
|
-
Some(Value::Map(m)) => Some(m.clone()),
|
|
68
|
-
_ => None,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// Merge: per-note overrides synth automation per key (volume/pan/pitch)
|
|
72
|
-
let automation = match (synth_automation, note_automation) {
|
|
73
|
-
(Some(mut a), Some(n)) => {
|
|
74
|
-
for (k, v) in n {
|
|
75
|
-
a.insert(k, v);
|
|
76
|
-
}
|
|
77
|
-
Some(a)
|
|
78
|
-
}
|
|
79
|
-
(None, Some(n)) => Some(n),
|
|
80
|
-
(Some(a), None) => Some(a),
|
|
81
|
-
_ => None,
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// If waveform references a plugin alias (e.g., alias.synth), use the WASM plugin runner
|
|
85
|
-
if waveform_str.contains('.') && waveform_str.ends_with(".synth") {
|
|
86
|
-
let alias = waveform_str.split('.').next().unwrap_or("");
|
|
87
|
-
if let Some(Value::String(uri)) = variable_table.get(alias) {
|
|
88
|
-
if let Some(id) = uri.strip_prefix("devalang://plugin/") {
|
|
89
|
-
let mut parts = id.split('.');
|
|
90
|
-
let author = parts.next().unwrap_or("");
|
|
91
|
-
let name = parts.next().unwrap_or("");
|
|
92
|
-
let key = format!("{}:{}", author, name);
|
|
93
|
-
if let Some((_info, wasm_bytes)) = global_store.plugins.get(&key) {
|
|
94
|
-
// Prepare buffer (stereo f32)
|
|
95
|
-
let sample_rate = 44100.0_f32;
|
|
96
|
-
let total_samples = ((duration_ms / 1000.0) * sample_rate) as usize;
|
|
97
|
-
let channels = 2usize;
|
|
98
|
-
let start_index = ((start_time * sample_rate) as usize) * channels;
|
|
99
|
-
let required_len = start_index + total_samples * channels;
|
|
100
|
-
if audio_engine.buffer.len() < required_len {
|
|
101
|
-
audio_engine.buffer.resize(required_len, 0);
|
|
102
|
-
}
|
|
103
|
-
let mut fbuf = vec![0.0f32; total_samples * channels];
|
|
104
|
-
let runner = WasmPluginRunner::new();
|
|
105
|
-
let mut params_num: std::collections::HashMap<String, f32> =
|
|
106
|
-
std::collections::HashMap::new();
|
|
107
|
-
let mut params_str: std::collections::HashMap<String, String> =
|
|
108
|
-
std::collections::HashMap::new();
|
|
109
|
-
for (k, v) in synth_params.iter() {
|
|
110
|
-
match v {
|
|
111
|
-
Value::Number(n) => {
|
|
112
|
-
params_num.insert(k.clone(), *n);
|
|
113
|
-
}
|
|
114
|
-
Value::String(s) => {
|
|
115
|
-
params_str.insert(k.clone(), s.clone());
|
|
116
|
-
}
|
|
117
|
-
Value::Identifier(s) => {
|
|
118
|
-
params_str.insert(k.clone(), s.clone());
|
|
119
|
-
}
|
|
120
|
-
_ => {}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
let _ = runner.render_note_with_params_in_place(
|
|
124
|
-
wasm_bytes,
|
|
125
|
-
&mut fbuf,
|
|
126
|
-
None,
|
|
127
|
-
final_freq,
|
|
128
|
-
amp_note,
|
|
129
|
-
duration_ms as i32,
|
|
130
|
-
44100,
|
|
131
|
-
2,
|
|
132
|
-
¶ms_num,
|
|
133
|
-
Some(¶ms_str),
|
|
134
|
-
);
|
|
135
|
-
for (i, sample) in fbuf.iter().enumerate().take(total_samples * channels) {
|
|
136
|
-
let s = (sample.clamp(-1.0, 1.0) * (i16::MAX as f32)) as i16;
|
|
137
|
-
let idx = start_index + i;
|
|
138
|
-
audio_engine.buffer[idx] = audio_engine.buffer[idx].saturating_add(s);
|
|
139
|
-
}
|
|
140
|
-
} else {
|
|
141
|
-
let logger = Logger::new();
|
|
142
|
-
logger.log_message(
|
|
143
|
-
LogLevel::Warning,
|
|
144
|
-
&format!(
|
|
145
|
-
"Plugin bytes not found for key '{}' (alias '{}').",
|
|
146
|
-
key, alias
|
|
147
|
-
),
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
151
|
-
let logger = Logger::new();
|
|
152
|
-
logger.log_message(
|
|
153
|
-
LogLevel::Warning,
|
|
154
|
-
&format!("Invalid plugin URI in alias '{}': {}", alias, uri),
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
} else {
|
|
158
|
-
let logger = Logger::new();
|
|
159
|
-
logger.log_message(
|
|
160
|
-
LogLevel::Warning,
|
|
161
|
-
&format!("Plugin alias '{}' not found in variable table.", alias),
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
} else {
|
|
165
|
-
// Allow types to adjust per-note scheduling/params
|
|
166
|
-
let start_ms = start_time * 1000.0;
|
|
167
|
-
let mut final_amp = amp_note;
|
|
168
|
-
let mut final_note_params = note_params.clone();
|
|
169
|
-
|
|
170
|
-
let mut handled = false;
|
|
171
|
-
if let Some(tval) = synth_params.get("type") {
|
|
172
|
-
let tname = match tval {
|
|
173
|
-
Value::String(s) => s.as_str(),
|
|
174
|
-
Value::Identifier(s) => s.as_str(),
|
|
175
|
-
_ => "",
|
|
176
|
-
};
|
|
177
|
-
match tname {
|
|
178
|
-
"arp" => {
|
|
179
|
-
// compute a step (ms) from synth params (rate/step). compute_arp_step
|
|
180
|
-
// will interpret `rate` as number of notes across the provided duration
|
|
181
|
-
let step_ms = crate::core::audio::interpreter::statements::arrow_call::types::arp::
|
|
182
|
-
compute_arp_step(duration_ms, 1, &synth_params);
|
|
183
|
-
let steps = if step_ms > 0.0 {
|
|
184
|
-
((duration_ms / step_ms).ceil() as usize).max(1)
|
|
185
|
-
} else {
|
|
186
|
-
1usize
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
// For each arp step, call prepare_note to get per-step params and schedule it
|
|
190
|
-
for idx in 0..steps {
|
|
191
|
-
let (start_abs_ms, freq_step, amp_out, params_out) =
|
|
192
|
-
crate::core::audio::interpreter::statements::arrow_call::types::arp::prepare_note(
|
|
193
|
-
¬e_name,
|
|
194
|
-
idx,
|
|
195
|
-
steps,
|
|
196
|
-
start_ms,
|
|
197
|
-
duration_ms,
|
|
198
|
-
amp_note,
|
|
199
|
-
&synth_params,
|
|
200
|
-
&final_note_params,
|
|
201
|
-
&automation,
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
// sub-note duration: default to step_ms so arp steps are audible and sequenced
|
|
205
|
-
let sub_duration_ms = if step_ms > 0.0 { step_ms } else { duration_ms };
|
|
206
|
-
|
|
207
|
-
audio_engine.insert_note(
|
|
208
|
-
waveform_str.to_string(),
|
|
209
|
-
freq_step,
|
|
210
|
-
amp_out,
|
|
211
|
-
start_abs_ms,
|
|
212
|
-
sub_duration_ms,
|
|
213
|
-
synth_params.clone(),
|
|
214
|
-
params_out.clone(),
|
|
215
|
-
automation.clone(),
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// mark handled to avoid the unconditional insert below
|
|
220
|
-
handled = true;
|
|
221
|
-
}
|
|
222
|
-
"pluck" => {
|
|
223
|
-
let (_s, _f, amp_out, params_out) =
|
|
224
|
-
crate::core::audio::interpreter::statements::arrow_call::types::pluck::prepare_note(
|
|
225
|
-
¬e_name,
|
|
226
|
-
0,
|
|
227
|
-
1,
|
|
228
|
-
start_ms,
|
|
229
|
-
duration_ms,
|
|
230
|
-
amp_note,
|
|
231
|
-
&synth_params,
|
|
232
|
-
&final_note_params,
|
|
233
|
-
&automation,
|
|
234
|
-
);
|
|
235
|
-
final_amp = amp_out;
|
|
236
|
-
final_note_params = params_out;
|
|
237
|
-
}
|
|
238
|
-
"pad" => {
|
|
239
|
-
let (_s, _f, amp_out, params_out) =
|
|
240
|
-
crate::core::audio::interpreter::statements::arrow_call::types::pad::prepare_note(
|
|
241
|
-
¬e_name,
|
|
242
|
-
0,
|
|
243
|
-
1,
|
|
244
|
-
start_ms,
|
|
245
|
-
duration_ms,
|
|
246
|
-
amp_note,
|
|
247
|
-
&synth_params,
|
|
248
|
-
&final_note_params,
|
|
249
|
-
&automation,
|
|
250
|
-
);
|
|
251
|
-
final_amp = amp_out;
|
|
252
|
-
final_note_params = params_out;
|
|
253
|
-
}
|
|
254
|
-
_ => {}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if !handled {
|
|
259
|
-
audio_engine.insert_note(
|
|
260
|
-
waveform_str.to_string(),
|
|
261
|
-
final_freq,
|
|
262
|
-
final_amp,
|
|
263
|
-
start_ms,
|
|
264
|
-
duration_ms,
|
|
265
|
-
synth_params.clone(),
|
|
266
|
-
final_note_params,
|
|
267
|
-
automation,
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
*max_end_time = (*max_end_time).max(end_time);
|
|
273
|
-
|
|
274
|
-
if update_cursor {
|
|
275
|
-
if let Some(c) = cursor_time.as_mut() {
|
|
276
|
-
**c = end_time;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return (*max_end_time, end_time);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
fn extract_f32(map: &HashMap<String, Value>, key: &str, base_bpm: f32) -> Option<f32> {
|
|
284
|
-
map.get(key).and_then(|v| match v {
|
|
285
|
-
Value::Number(n) => Some(*n),
|
|
286
|
-
Value::Beat(beat_str) => {
|
|
287
|
-
let parts: Vec<&str> = beat_str.split('/').collect();
|
|
288
|
-
if parts.len() == 2 {
|
|
289
|
-
let numerator = parts[0].parse::<f32>().ok()?;
|
|
290
|
-
let denominator = parts[1].parse::<f32>().ok()?;
|
|
291
|
-
|
|
292
|
-
Some((numerator / denominator) * ((60.0 / base_bpm) * 1000.0))
|
|
293
|
-
} else {
|
|
294
|
-
None
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
_ => None,
|
|
298
|
-
})
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
fn note_to_freq(note: &str) -> f32 {
|
|
302
|
-
let notes = [
|
|
303
|
-
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
|
|
304
|
-
];
|
|
305
|
-
|
|
306
|
-
if note.len() < 2 || note.len() > 3 {
|
|
307
|
-
return 440.0;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
let (name, octave_str) = note.split_at(note.len() - 1);
|
|
311
|
-
let semitone = notes.iter().position(|&n| n == name).unwrap_or(9) as i32;
|
|
312
|
-
let octave = octave_str.parse::<i32>().unwrap_or(4);
|
|
313
|
-
let midi_note = (octave + 1) * 12 + semitone;
|
|
314
|
-
|
|
315
|
-
440.0 * (2.0_f32).powf(((midi_note as f32) - 69.0) / 12.0)
|
|
316
|
-
}
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
use devalang_types::Value;
|
|
2
|
-
use std::collections::HashMap;
|
|
3
|
-
|
|
4
|
-
pub fn apply_defaults(synth_params: &mut HashMap<String, Value>) {
|
|
5
|
-
use devalang_types::Value as DV;
|
|
6
|
-
synth_params
|
|
7
|
-
.entry("attack".to_string())
|
|
8
|
-
.or_insert(DV::Number(1.0));
|
|
9
|
-
synth_params
|
|
10
|
-
.entry("decay".to_string())
|
|
11
|
-
.or_insert(DV::Number(50.0));
|
|
12
|
-
synth_params
|
|
13
|
-
.entry("sustain".to_string())
|
|
14
|
-
.or_insert(DV::Number(0.0));
|
|
15
|
-
synth_params
|
|
16
|
-
.entry("release".to_string())
|
|
17
|
-
.or_insert(DV::Number(120.0));
|
|
18
|
-
synth_params
|
|
19
|
-
.entry("glide".to_string())
|
|
20
|
-
.or_insert(DV::Boolean(false));
|
|
21
|
-
synth_params
|
|
22
|
-
.entry("slide".to_string())
|
|
23
|
-
.or_insert(DV::Boolean(false));
|
|
24
|
-
// arp defaults
|
|
25
|
-
synth_params
|
|
26
|
-
.entry("gate".to_string())
|
|
27
|
-
.or_insert(DV::Number(80.0));
|
|
28
|
-
synth_params
|
|
29
|
-
.entry("pattern".to_string())
|
|
30
|
-
.or_insert(DV::String("up".to_string()));
|
|
31
|
-
// per-step velocity shaping
|
|
32
|
-
synth_params
|
|
33
|
-
.entry("velocity_scale".to_string())
|
|
34
|
-
.or_insert(DV::Number(0.25));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Helper to compute an arp step and schedule notes; this is small and returns the computed step
|
|
38
|
-
pub fn compute_arp_step(
|
|
39
|
-
duration_ms: f32,
|
|
40
|
-
note_count: usize,
|
|
41
|
-
synth_params: &HashMap<String, Value>,
|
|
42
|
-
) -> f32 {
|
|
43
|
-
// New API: synth_params may contain "rate" (beats/duration string or number),
|
|
44
|
-
// "pattern" (up, down, updown), and "step" (ms or number)
|
|
45
|
-
use devalang_types::Duration as D;
|
|
46
|
-
|
|
47
|
-
// If rate is provided as number (notes across duration), use it
|
|
48
|
-
if let Some(Value::Number(rate)) = synth_params.get("rate") {
|
|
49
|
-
if *rate > 0.0 {
|
|
50
|
-
return duration_ms / *rate;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// If rate provided as Duration/Beat/String parse as musical fraction -> seconds
|
|
55
|
-
if let Some(v) = synth_params.get("rate") {
|
|
56
|
-
match v {
|
|
57
|
-
Value::Duration(d) => match d {
|
|
58
|
-
D::Number(sec) => {
|
|
59
|
-
if *sec > 0.0 {
|
|
60
|
-
return sec * 1000.0;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
D::Beat(_) | D::Identifier(_) => {
|
|
64
|
-
// need bpm; fallback 120
|
|
65
|
-
let bpm = synth_params
|
|
66
|
-
.get("bpm")
|
|
67
|
-
.and_then(|bv| {
|
|
68
|
-
if let Value::Number(n) = bv {
|
|
69
|
-
Some(*n)
|
|
70
|
-
} else {
|
|
71
|
-
None
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
.unwrap_or(120.0);
|
|
75
|
-
if let Some(sec) =
|
|
76
|
-
crate::core::audio::engine::driver::duration_to_seconds(d, bpm)
|
|
77
|
-
{
|
|
78
|
-
if sec > 0.0 {
|
|
79
|
-
return sec * 1000.0;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
_ => {}
|
|
84
|
-
},
|
|
85
|
-
Value::String(s) | Value::Identifier(s) => {
|
|
86
|
-
let bpm = synth_params
|
|
87
|
-
.get("bpm")
|
|
88
|
-
.and_then(|bv| {
|
|
89
|
-
if let Value::Number(n) = bv {
|
|
90
|
-
Some(*n)
|
|
91
|
-
} else {
|
|
92
|
-
None
|
|
93
|
-
}
|
|
94
|
-
})
|
|
95
|
-
.unwrap_or(120.0);
|
|
96
|
-
if let Some(sec) =
|
|
97
|
-
crate::core::audio::engine::driver::parse_fraction_to_seconds(s, bpm)
|
|
98
|
-
{
|
|
99
|
-
if sec > 0.0 {
|
|
100
|
-
return sec * 1000.0;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
if let Ok(n) = s.parse::<f32>() {
|
|
104
|
-
if n > 0.0 {
|
|
105
|
-
return duration_ms / n;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
_ => {}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// fallback to explicit step param
|
|
114
|
-
let default_step = (duration_ms / (note_count as f32)) * 0.5;
|
|
115
|
-
match synth_params.get("step") {
|
|
116
|
-
Some(Value::Number(n)) => *n,
|
|
117
|
-
Some(Value::String(s)) => s.parse::<f32>().unwrap_or(default_step),
|
|
118
|
-
Some(Value::Identifier(s)) => s.parse::<f32>().unwrap_or(default_step),
|
|
119
|
-
_ => default_step,
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Ensure arp defaults include an arp_step if provided as beats / number string
|
|
124
|
-
pub fn ensure_arp_step(synth_params: &mut HashMap<String, Value>, default_step: f32) {
|
|
125
|
-
use devalang_types::Value as DV;
|
|
126
|
-
if !synth_params.contains_key("arp_step") {
|
|
127
|
-
synth_params.insert("arp_step".to_string(), DV::Number(default_step));
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Prepare per-note scheduling & params for arp
|
|
132
|
-
pub fn prepare_note(
|
|
133
|
-
note_name: &str,
|
|
134
|
-
index: usize,
|
|
135
|
-
total: usize,
|
|
136
|
-
start_time_ms: f32,
|
|
137
|
-
duration_ms: f32,
|
|
138
|
-
amp: f32,
|
|
139
|
-
synth_params: &HashMap<String, Value>,
|
|
140
|
-
note_params: &HashMap<String, Value>,
|
|
141
|
-
_automation: &Option<HashMap<String, Value>>,
|
|
142
|
-
) -> (f32, f32, f32, HashMap<String, Value>) {
|
|
143
|
-
// compute a slightly syncopated step: half-step + small swing
|
|
144
|
-
let mut step = compute_arp_step(duration_ms, total, synth_params);
|
|
145
|
-
// introduce simple swing based on index
|
|
146
|
-
let swing = match synth_params.get("swing") {
|
|
147
|
-
Some(Value::Number(n)) => *n,
|
|
148
|
-
_ => 0.05,
|
|
149
|
-
};
|
|
150
|
-
if index % 2 == 1 {
|
|
151
|
-
step *= 1.0 + swing;
|
|
152
|
-
}
|
|
153
|
-
let start = start_time_ms + (index as f32) * step;
|
|
154
|
-
// For now, arp doesn't alter amp or params beyond start offset
|
|
155
|
-
let amp_out = amp;
|
|
156
|
-
let mut params_out = note_params.clone();
|
|
157
|
-
// optionally expose arp-specific params into note params
|
|
158
|
-
if let Some(Value::Number(n)) = synth_params.get("gate") {
|
|
159
|
-
// normalize gate to 0.0-1.0 if >1 (percent)
|
|
160
|
-
let gate_val = if *n > 1.0 { (*n) / 100.0 } else { *n };
|
|
161
|
-
params_out
|
|
162
|
-
.entry("gate".to_string())
|
|
163
|
-
.or_insert(Value::Number(gate_val));
|
|
164
|
-
}
|
|
165
|
-
// simple velocity curve across arp steps (so later steps can be quieter/louder)
|
|
166
|
-
if let Some(Value::Number(vscale)) = synth_params.get("velocity_scale") {
|
|
167
|
-
let mult = 1.0 - ((index as f32) / (total as f32)) * (*vscale);
|
|
168
|
-
params_out.insert("velocity".to_string(), Value::Number(mult.max(0.0)));
|
|
169
|
-
}
|
|
170
|
-
// propagate pattern if present (engine or higher layer may use it)
|
|
171
|
-
if let Some(Value::String(p)) = synth_params.get("pattern") {
|
|
172
|
-
params_out.insert("pattern".to_string(), Value::String(p.clone()));
|
|
173
|
-
}
|
|
174
|
-
(start, note_to_freq(note_name), amp_out, params_out)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
fn note_to_freq(note: &str) -> f32 {
|
|
178
|
-
let notes = [
|
|
179
|
-
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
|
|
180
|
-
];
|
|
181
|
-
|
|
182
|
-
if note.len() < 2 || note.len() > 3 {
|
|
183
|
-
return 440.0;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
let (name, octave_str) = note.split_at(note.len() - 1);
|
|
187
|
-
let semitone = notes.iter().position(|&n| n == name).unwrap_or(9) as i32;
|
|
188
|
-
let octave = octave_str.parse::<i32>().unwrap_or(4);
|
|
189
|
-
let midi_note = (octave + 1) * 12 + semitone;
|
|
190
|
-
|
|
191
|
-
440.0 * (2.0_f32).powf(((midi_note as f32) - 69.0) / 12.0)
|
|
192
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
pub mod arp;
|
|
2
|
-
pub mod pad;
|
|
3
|
-
pub mod pluck;
|
|
4
|
-
pub mod sub;
|
|
5
|
-
|
|
6
|
-
use devalang_types::Value;
|
|
7
|
-
use std::collections::HashMap;
|
|
8
|
-
|
|
9
|
-
pub fn apply_type(synth_params: &mut HashMap<String, Value>) {
|
|
10
|
-
if let Some(tval) = synth_params.get("type") {
|
|
11
|
-
let tname = match tval {
|
|
12
|
-
Value::String(s) => s.as_str(),
|
|
13
|
-
Value::Identifier(s) => s.as_str(),
|
|
14
|
-
_ => "",
|
|
15
|
-
};
|
|
16
|
-
match tname {
|
|
17
|
-
"pad" => pad::apply_defaults(synth_params),
|
|
18
|
-
"pluck" => pluck::apply_defaults(synth_params),
|
|
19
|
-
"arp" => arp::apply_defaults(synth_params),
|
|
20
|
-
"sub" => sub::apply_defaults(synth_params),
|
|
21
|
-
_ => {}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
use devalang_types::Value;
|
|
2
|
-
use std::collections::HashMap;
|
|
3
|
-
|
|
4
|
-
pub fn apply_defaults(synth_params: &mut HashMap<String, Value>) {
|
|
5
|
-
use devalang_types::Value as DV;
|
|
6
|
-
// pad: long attack/release, high sustain (0.0-1.0)
|
|
7
|
-
synth_params
|
|
8
|
-
.entry("attack".to_string())
|
|
9
|
-
.or_insert(DV::Number(600.0));
|
|
10
|
-
synth_params
|
|
11
|
-
.entry("decay".to_string())
|
|
12
|
-
.or_insert(DV::Number(300.0));
|
|
13
|
-
synth_params
|
|
14
|
-
.entry("sustain".to_string())
|
|
15
|
-
.or_insert(DV::Number(0.8));
|
|
16
|
-
synth_params
|
|
17
|
-
.entry("release".to_string())
|
|
18
|
-
.or_insert(DV::Number(900.0));
|
|
19
|
-
// optional unison/voices parameters
|
|
20
|
-
synth_params
|
|
21
|
-
.entry("voices".to_string())
|
|
22
|
-
.or_insert(DV::Number(3.0));
|
|
23
|
-
synth_params
|
|
24
|
-
.entry("unison_detune".to_string())
|
|
25
|
-
.or_insert(DV::Number(15.0));
|
|
26
|
-
// slightly wider default chorus/depth for pads
|
|
27
|
-
if !synth_params.contains_key("effects") {
|
|
28
|
-
let mut ch: std::collections::HashMap<String, Value> = std::collections::HashMap::new();
|
|
29
|
-
ch.insert("type".to_string(), Value::String("chorus".to_string()));
|
|
30
|
-
ch.insert("depth".to_string(), Value::Number(0.12));
|
|
31
|
-
ch.insert("rate".to_string(), Value::Number(0.25));
|
|
32
|
-
synth_params.insert("effects".to_string(), DV::Array(vec![DV::Map(ch)]));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Prepare per-note modifications for pad: longer release/attack, gentle detune per-voice
|
|
37
|
-
pub fn prepare_note(
|
|
38
|
-
_note_name: &str,
|
|
39
|
-
index: usize,
|
|
40
|
-
total: usize,
|
|
41
|
-
_start_time_ms: f32,
|
|
42
|
-
_duration_ms: f32,
|
|
43
|
-
amp: f32,
|
|
44
|
-
synth_params: &HashMap<String, Value>,
|
|
45
|
-
note_params: &HashMap<String, Value>,
|
|
46
|
-
_automation: &Option<HashMap<String, Value>>,
|
|
47
|
-
) -> (f32, f32, f32, HashMap<String, Value>) {
|
|
48
|
-
let mut params_out = note_params.clone();
|
|
49
|
-
// inject pad envelope defaults if missing
|
|
50
|
-
params_out
|
|
51
|
-
.entry("attack".to_string())
|
|
52
|
-
.or_insert(Value::Number(600.0));
|
|
53
|
-
params_out
|
|
54
|
-
.entry("release".to_string())
|
|
55
|
-
.or_insert(Value::Number(900.0));
|
|
56
|
-
params_out
|
|
57
|
-
.entry("sustain".to_string())
|
|
58
|
-
.or_insert(Value::Number(0.8));
|
|
59
|
-
|
|
60
|
-
// propagate unison/voices into per-note params if synth provided
|
|
61
|
-
if let Some(Value::Number(v)) = synth_params.get("voices") {
|
|
62
|
-
params_out.insert("voices".to_string(), Value::Number(*v));
|
|
63
|
-
}
|
|
64
|
-
if let Some(Value::Number(d)) = synth_params.get("unison_detune") {
|
|
65
|
-
params_out.insert("unison_detune".to_string(), Value::Number(*d));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// gentle detune based on index and synth param "detune" if present
|
|
69
|
-
// gentle detune based on synth detune or unison settings
|
|
70
|
-
let detune_base = match synth_params.get("detune") {
|
|
71
|
-
Some(Value::Number(n)) => *n,
|
|
72
|
-
_ => 0.0,
|
|
73
|
-
};
|
|
74
|
-
let mut detune = detune_base * ((index as f32) / (total as f32));
|
|
75
|
-
// if unison used, spread voices around center
|
|
76
|
-
if let Some(Value::Number(_voices)) = synth_params.get("voices") {
|
|
77
|
-
if let Some(Value::Number(ud)) = synth_params.get("unison_detune") {
|
|
78
|
-
// spread by a fraction based on voice index
|
|
79
|
-
detune += (*ud) * (((index as f32) - (total as f32 - 1.0) / 2.0) / (total as f32));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
params_out.insert("detune".to_string(), Value::Number(detune));
|
|
84
|
-
|
|
85
|
-
// compute frequency with detune in cents
|
|
86
|
-
let freq = note_to_freq(_note_name) * (2.0_f32).powf(detune / 1200.0);
|
|
87
|
-
|
|
88
|
-
// add a subtle chorus placeholder effect if none present (engine may ignore unknown effects)
|
|
89
|
-
use devalang_types::Value as DV;
|
|
90
|
-
if !params_out.contains_key("effects") {
|
|
91
|
-
let mut ch: std::collections::HashMap<String, Value> = std::collections::HashMap::new();
|
|
92
|
-
ch.insert("type".to_string(), Value::String("chorus".to_string()));
|
|
93
|
-
ch.insert("depth".to_string(), Value::Number(0.15));
|
|
94
|
-
ch.insert("rate".to_string(), Value::Number(0.25));
|
|
95
|
-
params_out.insert("effects".to_string(), DV::Array(vec![DV::Map(ch)]));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
(0.0, freq, amp, params_out)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
fn note_to_freq(note: &str) -> f32 {
|
|
102
|
-
let notes = [
|
|
103
|
-
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
|
|
104
|
-
];
|
|
105
|
-
|
|
106
|
-
if note.len() < 2 || note.len() > 3 {
|
|
107
|
-
return 440.0;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let (name, octave_str) = note.split_at(note.len() - 1);
|
|
111
|
-
let semitone = notes.iter().position(|&n| n == name).unwrap_or(9) as i32;
|
|
112
|
-
let octave = octave_str.parse::<i32>().unwrap_or(4);
|
|
113
|
-
let midi_note = (octave + 1) * 12 + semitone;
|
|
114
|
-
|
|
115
|
-
440.0 * (2.0_f32).powf(((midi_note as f32) - 69.0) / 12.0)
|
|
116
|
-
}
|