@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.
Files changed (159) hide show
  1. package/Cargo.toml +84 -81
  2. package/README.md +3 -2
  3. package/docs/CHANGELOG.md +41 -0
  4. package/docs/ROADMAP.md +3 -3
  5. package/examples/chain.deva +19 -0
  6. package/examples/plugin.deva +10 -10
  7. package/examples/routing.deva +23 -0
  8. package/out-tsc/bin/project-version.json +6 -0
  9. package/out-tsc/pkg/devalang_core_bg.wasm.d.ts +8 -8
  10. package/out-tsc/scripts/version/copy-to-binary.d.ts +1 -0
  11. package/out-tsc/scripts/version/copy-to-binary.js +79 -0
  12. package/package.json +23 -10
  13. package/project-version.json +3 -3
  14. package/rust/bindings/Cargo.toml +9 -0
  15. package/rust/bindings/src/lib.rs +86 -0
  16. package/rust/cli/addon/commands.rs +35 -0
  17. package/rust/cli/addon/download.rs +234 -0
  18. package/rust/cli/addon/install.rs +33 -0
  19. package/rust/cli/addon/list.rs +224 -0
  20. package/rust/cli/addon/metadata.rs +124 -0
  21. package/rust/cli/addon/mod.rs +8 -0
  22. package/rust/cli/addon/remove.rs +271 -0
  23. package/rust/cli/addon/update.rs +305 -0
  24. package/rust/cli/{install/addon.rs → addon/utils.rs} +109 -118
  25. package/rust/cli/build/commands.rs +153 -153
  26. package/rust/cli/build/process.rs +165 -165
  27. package/rust/cli/check/mod.rs +208 -208
  28. package/rust/cli/discover/commands.rs +275 -253
  29. package/rust/cli/discover/config.rs +109 -111
  30. package/rust/cli/discover/fs.rs +19 -19
  31. package/rust/cli/discover/install.rs +214 -103
  32. package/rust/cli/discover/metadata.rs +48 -48
  33. package/rust/cli/discover/mod.rs +5 -5
  34. package/rust/cli/me/commands.rs +52 -0
  35. package/rust/cli/me/mod.rs +1 -0
  36. package/rust/cli/mod.rs +12 -12
  37. package/rust/cli/parser.rs +30 -69
  38. package/rust/cli/play/commands.rs +375 -375
  39. package/rust/cli/play/process.rs +159 -159
  40. package/rust/core/audio/engine/driver.rs +19 -2
  41. package/rust/core/audio/engine/export.rs +169 -169
  42. package/rust/core/audio/engine/mod.rs +56 -56
  43. package/rust/core/audio/engine/notes/dsp.rs +88 -85
  44. package/rust/core/audio/engine/notes/mod.rs +53 -44
  45. package/rust/core/audio/engine/notes/params.rs +294 -294
  46. package/rust/core/audio/engine/sample/insert.rs +148 -47
  47. package/rust/core/audio/engine/sample/mod.rs +40 -40
  48. package/rust/core/audio/engine/sample/padding.rs +170 -170
  49. package/rust/core/audio/evaluator/condition.rs +61 -61
  50. package/rust/core/audio/evaluator/numeric.rs +152 -152
  51. package/rust/core/audio/evaluator/rhs.rs +16 -16
  52. package/rust/core/audio/evaluator/string_expr.rs +94 -94
  53. package/rust/core/audio/interpreter/driver.rs +574 -574
  54. package/rust/core/audio/interpreter/mod.rs +2 -2
  55. package/rust/core/audio/interpreter/statements/arrow_call/interprete.rs +9 -5
  56. package/rust/core/audio/interpreter/statements/arrow_call/methods/chord.rs +398 -384
  57. package/rust/core/audio/interpreter/statements/arrow_call/methods/effects.rs +323 -0
  58. package/rust/core/audio/interpreter/statements/arrow_call/methods/mod.rs +1 -0
  59. package/rust/core/audio/interpreter/statements/arrow_call/methods/note.rs +66 -11
  60. package/rust/core/audio/interpreter/statements/arrow_call/mod.rs +3 -3
  61. package/rust/core/audio/interpreter/statements/arrow_call/types/arp.rs +192 -192
  62. package/rust/core/audio/interpreter/statements/arrow_call/types/mod.rs +24 -24
  63. package/rust/core/audio/interpreter/statements/arrow_call/types/pad.rs +116 -116
  64. package/rust/core/audio/interpreter/statements/arrow_call/types/pluck.rs +97 -97
  65. package/rust/core/audio/interpreter/statements/arrow_call/types/sub.rs +100 -100
  66. package/rust/core/audio/interpreter/statements/automate.rs +16 -16
  67. package/rust/core/audio/interpreter/statements/call.rs +31 -1
  68. package/rust/core/audio/interpreter/statements/condition.rs +72 -72
  69. package/rust/core/audio/interpreter/statements/function.rs +24 -24
  70. package/rust/core/audio/interpreter/statements/let_.rs +36 -36
  71. package/rust/core/audio/interpreter/statements/load.rs +17 -17
  72. package/rust/core/audio/interpreter/statements/loop_.rs +115 -115
  73. package/rust/core/audio/interpreter/statements/spawn.rs +51 -2
  74. package/rust/core/audio/interpreter/statements/trigger.rs +242 -239
  75. package/rust/core/audio/loader/trigger.rs +98 -98
  76. package/rust/core/audio/player.rs +70 -70
  77. package/rust/core/audio/special/mod.rs +9 -9
  78. package/rust/core/builder/mod.rs +129 -129
  79. package/rust/core/debugger/lexer.rs +27 -27
  80. package/rust/core/debugger/logs.rs +52 -52
  81. package/rust/core/debugger/preprocessor.rs +27 -27
  82. package/rust/core/debugger/store.rs +38 -38
  83. package/rust/core/lexer/driver.rs +59 -59
  84. package/rust/core/lexer/handler/arrow.rs +82 -82
  85. package/rust/core/lexer/handler/at.rs +21 -21
  86. package/rust/core/lexer/handler/brace.rs +41 -41
  87. package/rust/core/lexer/handler/colon.rs +21 -21
  88. package/rust/core/lexer/handler/comment.rs +30 -30
  89. package/rust/core/lexer/handler/dot.rs +21 -21
  90. package/rust/core/lexer/handler/driver.rs +337 -337
  91. package/rust/core/lexer/handler/identifier.rs +47 -47
  92. package/rust/core/lexer/handler/indent.rs +66 -66
  93. package/rust/core/lexer/handler/mod.rs +15 -15
  94. package/rust/core/lexer/handler/newline.rs +23 -23
  95. package/rust/core/lexer/handler/number.rs +31 -31
  96. package/rust/core/lexer/handler/operator.rs +46 -46
  97. package/rust/core/lexer/handler/parenthesis.rs +41 -41
  98. package/rust/core/lexer/handler/slash.rs +21 -21
  99. package/rust/core/lexer/handler/string.rs +63 -63
  100. package/rust/core/lexer/mod.rs +3 -3
  101. package/rust/core/mod.rs +9 -9
  102. package/rust/core/parser/driver/block.rs +111 -111
  103. package/rust/core/parser/driver/cursor.rs +82 -82
  104. package/rust/core/parser/driver/driver_impl.rs +21 -1
  105. package/rust/core/parser/driver/mod.rs +6 -6
  106. package/rust/core/parser/driver/parse_array.rs +120 -120
  107. package/rust/core/parser/driver/parse_map.rs +247 -223
  108. package/rust/core/parser/driver/parser.rs +160 -160
  109. package/rust/core/parser/handler/arrow_call.rs +65 -14
  110. package/rust/core/parser/handler/identifier/synth.rs +171 -135
  111. package/rust/core/parser/handler/mod.rs +9 -9
  112. package/rust/core/parser/handler/pattern.rs +24 -1
  113. package/rust/core/plugin/loader.rs +137 -137
  114. package/rust/core/plugin/mod.rs +2 -2
  115. package/rust/core/plugin/runner/non_wasm.rs +481 -297
  116. package/rust/core/plugin/runner/wasm32.rs +1 -0
  117. package/rust/core/preprocessor/loader/inject.rs +313 -278
  118. package/rust/core/preprocessor/loader/loader_helpers.rs +110 -110
  119. package/rust/core/preprocessor/loader/mod.rs +235 -235
  120. package/rust/core/preprocessor/module.rs +55 -55
  121. package/rust/core/preprocessor/processor/handlers.rs +107 -107
  122. package/rust/core/preprocessor/resolver/bank.rs +49 -49
  123. package/rust/core/preprocessor/resolver/call.rs +124 -124
  124. package/rust/core/preprocessor/resolver/condition.rs +95 -95
  125. package/rust/core/preprocessor/resolver/driver.rs +324 -324
  126. package/rust/core/preprocessor/resolver/function.rs +69 -69
  127. package/rust/core/preprocessor/resolver/group.rs +122 -122
  128. package/rust/core/preprocessor/resolver/let_.rs +32 -32
  129. package/rust/core/preprocessor/resolver/loop_.rs +318 -318
  130. package/rust/core/preprocessor/resolver/mod.rs +16 -16
  131. package/rust/core/preprocessor/resolver/pattern.rs +95 -83
  132. package/rust/core/preprocessor/resolver/spawn.rs +99 -99
  133. package/rust/core/preprocessor/resolver/synth.rs +54 -54
  134. package/rust/core/preprocessor/resolver/tempo.rs +48 -48
  135. package/rust/core/preprocessor/resolver/trigger.rs +116 -116
  136. package/rust/core/preprocessor/resolver/value.rs +176 -176
  137. package/rust/core/store/global.rs +57 -57
  138. package/rust/lib.rs +323 -323
  139. package/rust/macros/Cargo.toml +14 -0
  140. package/rust/macros/src/lib.rs +52 -0
  141. package/rust/main.rs +311 -142
  142. package/rust/types/Cargo.toml +1 -1
  143. package/rust/types/src/addons.rs +3 -1
  144. package/rust/types/src/config.rs +1 -3
  145. package/rust/utils/Cargo.toml +5 -2
  146. package/rust/utils/src/file.rs +397 -14
  147. package/rust/utils/src/path.rs +31 -2
  148. package/rust/utils/src/version.rs +38 -7
  149. package/rust/web/auth.rs +5 -0
  150. package/rust/web/forge.rs +5 -0
  151. package/rust/web/mod.rs +5 -3
  152. package/typescript/scripts/version/copy-to-binary.ts +82 -0
  153. package/rust/cli/bank/api.rs +0 -122
  154. package/rust/cli/bank/commands.rs +0 -306
  155. package/rust/cli/bank/mod.rs +0 -29
  156. package/rust/cli/install/bank.rs +0 -72
  157. package/rust/cli/install/commands.rs +0 -35
  158. package/rust/cli/install/mod.rs +0 -4
  159. package/rust/cli/install/plugin.rs +0 -80
@@ -1,192 +1,192 @@
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
+ 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 +1,24 @@
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
+ 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
+ }