@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,169 +1,169 @@
1
- use crate::core::audio::engine::driver::MidiNoteEvent;
2
- use std::fs::File;
3
-
4
- pub fn generate_midi_file_impl(
5
- midi_events: &Vec<MidiNoteEvent>,
6
- output_path: &String,
7
- bpm: Option<f32>,
8
- tpqn: Option<u16>,
9
- ) -> Result<(), String> {
10
- use midly::num::{u7, u15, u24};
11
- use midly::{
12
- Format, Header, MetaMessage, MidiMessage, Smf, Timing, TrackEvent, TrackEventKind,
13
- };
14
-
15
- if midi_events.is_empty() {
16
- return Ok(());
17
- }
18
-
19
- let bpm = bpm.unwrap_or(120.0_f32);
20
- let tpqn: u16 = tpqn.unwrap_or(480u16);
21
- let header = Header {
22
- format: Format::SingleTrack,
23
- timing: Timing::Metrical(u15::from(tpqn)),
24
- };
25
-
26
- #[derive(Clone)]
27
- struct AbsEvent {
28
- tick: u64,
29
- kind: TrackEventKind<'static>,
30
- }
31
-
32
- let mut abs_events: Vec<AbsEvent> = Vec::new();
33
- let microsecs_per_quarter = (60_000_000.0 / bpm) as u32;
34
- abs_events.push(AbsEvent {
35
- tick: 0,
36
- kind: TrackEventKind::Meta(MetaMessage::Tempo(u24::from(microsecs_per_quarter))),
37
- });
38
-
39
- for ev in midi_events {
40
- let start_secs = (ev.start_ms as f32) / 1000.0;
41
- let dur_secs = (ev.duration_ms as f32) / 1000.0;
42
- let start_ticks_f = start_secs * (bpm / 60.0) * (tpqn as f32);
43
- let dur_ticks_f = dur_secs * (bpm / 60.0) * (tpqn as f32);
44
- let start_tick = start_ticks_f.max(0.0).round() as u64;
45
- let off_tick = (start_ticks_f + dur_ticks_f).max(start_tick as f32).round() as u64;
46
-
47
- let key = u7::from(ev.key.min(127));
48
- let vel = u7::from(ev.vel.min(127));
49
-
50
- abs_events.push(AbsEvent {
51
- tick: start_tick,
52
- kind: TrackEventKind::Midi {
53
- channel: (ev.channel as u8).into(),
54
- message: MidiMessage::NoteOn { key, vel },
55
- },
56
- });
57
-
58
- abs_events.push(AbsEvent {
59
- tick: off_tick,
60
- kind: TrackEventKind::Midi {
61
- channel: (ev.channel as u8).into(),
62
- message: MidiMessage::NoteOff {
63
- key,
64
- vel: u7::from(0),
65
- },
66
- },
67
- });
68
- }
69
-
70
- let max_tick = abs_events.iter().map(|e| e.tick).max().unwrap_or(0);
71
- abs_events.push(AbsEvent {
72
- tick: max_tick + 0,
73
- kind: TrackEventKind::Meta(MetaMessage::EndOfTrack),
74
- });
75
- abs_events.sort_by_key(|e| e.tick);
76
-
77
- let mut track: Vec<TrackEvent> = Vec::new();
78
- let mut last_tick: u64 = 0;
79
- for e in abs_events {
80
- let delta = (e.tick - last_tick) as u32;
81
- track.push(TrackEvent {
82
- delta: delta.into(),
83
- kind: e.kind,
84
- });
85
- last_tick = e.tick;
86
- }
87
-
88
- let smf = Smf {
89
- header,
90
- tracks: vec![track],
91
- };
92
-
93
- if let Ok(mut file) = File::create(output_path) {
94
- if let Err(e) = smf.write_std(&mut file) {
95
- return Err(format!("Error writing MIDI file: {}", e));
96
- }
97
- } else {
98
- return Err(format!("Cannot create MIDI file at {}", output_path));
99
- }
100
-
101
- Ok(())
102
- }
103
-
104
- pub fn generate_wav_file_impl(
105
- buffer: &mut Vec<i16>,
106
- output_dir: &String,
107
- audio_format: Option<String>,
108
- sample_rate: Option<u32>,
109
- ) -> Result<(), String> {
110
- if buffer.len() % (crate::core::audio::engine::CHANNELS as usize) != 0 {
111
- buffer.push(0);
112
- }
113
-
114
- let sr = sample_rate.unwrap_or(crate::core::audio::engine::SAMPLE_RATE);
115
- let format_str = audio_format.unwrap_or_else(|| "Wav16".to_string());
116
- let fmt_low = format_str.to_lowercase();
117
-
118
- match fmt_low.as_str() {
119
- "wav16" => {
120
- let spec = hound::WavSpec {
121
- channels: crate::core::audio::engine::CHANNELS,
122
- sample_rate: sr,
123
- bits_per_sample: 16,
124
- sample_format: hound::SampleFormat::Int,
125
- };
126
-
127
- let mut writer = hound::WavWriter::create(output_dir, spec)
128
- .map_err(|e| format!("Error creating WAV file: {}", e))?;
129
-
130
- for sample in buffer.iter() {
131
- writer
132
- .write_sample(*sample)
133
- .map_err(|e| format!("Error writing sample: {:?}", e))?;
134
- }
135
-
136
- writer
137
- .finalize()
138
- .map_err(|e| format!("Error finalizing WAV: {:?}", e))?;
139
- }
140
- "wav24" | "wav32" => {
141
- let bits = if fmt_low.contains("24") { 24 } else { 32 };
142
- let spec = hound::WavSpec {
143
- channels: crate::core::audio::engine::CHANNELS,
144
- sample_rate: sr,
145
- bits_per_sample: bits,
146
- sample_format: hound::SampleFormat::Int,
147
- };
148
-
149
- let mut writer = hound::WavWriter::create(output_dir, spec)
150
- .map_err(|e| format!("Error creating WAV file: {}", e))?;
151
-
152
- for &s in buffer.iter() {
153
- let v32 = (s as i32) << (bits - 16);
154
- writer
155
- .write_sample(v32)
156
- .map_err(|e| format!("Error writing sample: {:?}", e))?;
157
- }
158
-
159
- writer
160
- .finalize()
161
- .map_err(|e| format!("Error finalizing WAV: {:?}", e))?;
162
- }
163
- _ => {
164
- return Err(format!("Unsupported audio format: {}", format_str));
165
- }
166
- }
167
-
168
- Ok(())
169
- }
1
+ use crate::core::audio::engine::driver::MidiNoteEvent;
2
+ use std::fs::File;
3
+
4
+ pub fn generate_midi_file_impl(
5
+ midi_events: &Vec<MidiNoteEvent>,
6
+ output_path: &String,
7
+ bpm: Option<f32>,
8
+ tpqn: Option<u16>,
9
+ ) -> Result<(), String> {
10
+ use midly::num::{u7, u15, u24};
11
+ use midly::{
12
+ Format, Header, MetaMessage, MidiMessage, Smf, Timing, TrackEvent, TrackEventKind,
13
+ };
14
+
15
+ if midi_events.is_empty() {
16
+ return Ok(());
17
+ }
18
+
19
+ let bpm = bpm.unwrap_or(120.0_f32);
20
+ let tpqn: u16 = tpqn.unwrap_or(480u16);
21
+ let header = Header {
22
+ format: Format::SingleTrack,
23
+ timing: Timing::Metrical(u15::from(tpqn)),
24
+ };
25
+
26
+ #[derive(Clone)]
27
+ struct AbsEvent {
28
+ tick: u64,
29
+ kind: TrackEventKind<'static>,
30
+ }
31
+
32
+ let mut abs_events: Vec<AbsEvent> = Vec::new();
33
+ let microsecs_per_quarter = (60_000_000.0 / bpm) as u32;
34
+ abs_events.push(AbsEvent {
35
+ tick: 0,
36
+ kind: TrackEventKind::Meta(MetaMessage::Tempo(u24::from(microsecs_per_quarter))),
37
+ });
38
+
39
+ for ev in midi_events {
40
+ let start_secs = (ev.start_ms as f32) / 1000.0;
41
+ let dur_secs = (ev.duration_ms as f32) / 1000.0;
42
+ let start_ticks_f = start_secs * (bpm / 60.0) * (tpqn as f32);
43
+ let dur_ticks_f = dur_secs * (bpm / 60.0) * (tpqn as f32);
44
+ let start_tick = start_ticks_f.max(0.0).round() as u64;
45
+ let off_tick = (start_ticks_f + dur_ticks_f).max(start_tick as f32).round() as u64;
46
+
47
+ let key = u7::from(ev.key.min(127));
48
+ let vel = u7::from(ev.vel.min(127));
49
+
50
+ abs_events.push(AbsEvent {
51
+ tick: start_tick,
52
+ kind: TrackEventKind::Midi {
53
+ channel: (ev.channel as u8).into(),
54
+ message: MidiMessage::NoteOn { key, vel },
55
+ },
56
+ });
57
+
58
+ abs_events.push(AbsEvent {
59
+ tick: off_tick,
60
+ kind: TrackEventKind::Midi {
61
+ channel: (ev.channel as u8).into(),
62
+ message: MidiMessage::NoteOff {
63
+ key,
64
+ vel: u7::from(0),
65
+ },
66
+ },
67
+ });
68
+ }
69
+
70
+ let max_tick = abs_events.iter().map(|e| e.tick).max().unwrap_or(0);
71
+ abs_events.push(AbsEvent {
72
+ tick: max_tick + 0,
73
+ kind: TrackEventKind::Meta(MetaMessage::EndOfTrack),
74
+ });
75
+ abs_events.sort_by_key(|e| e.tick);
76
+
77
+ let mut track: Vec<TrackEvent> = Vec::new();
78
+ let mut last_tick: u64 = 0;
79
+ for e in abs_events {
80
+ let delta = (e.tick - last_tick) as u32;
81
+ track.push(TrackEvent {
82
+ delta: delta.into(),
83
+ kind: e.kind,
84
+ });
85
+ last_tick = e.tick;
86
+ }
87
+
88
+ let smf = Smf {
89
+ header,
90
+ tracks: vec![track],
91
+ };
92
+
93
+ if let Ok(mut file) = File::create(output_path) {
94
+ if let Err(e) = smf.write_std(&mut file) {
95
+ return Err(format!("Error writing MIDI file: {}", e));
96
+ }
97
+ } else {
98
+ return Err(format!("Cannot create MIDI file at {}", output_path));
99
+ }
100
+
101
+ Ok(())
102
+ }
103
+
104
+ pub fn generate_wav_file_impl(
105
+ buffer: &mut Vec<i16>,
106
+ output_dir: &String,
107
+ audio_format: Option<String>,
108
+ sample_rate: Option<u32>,
109
+ ) -> Result<(), String> {
110
+ if buffer.len() % (crate::core::audio::engine::CHANNELS as usize) != 0 {
111
+ buffer.push(0);
112
+ }
113
+
114
+ let sr = sample_rate.unwrap_or(crate::core::audio::engine::SAMPLE_RATE);
115
+ let format_str = audio_format.unwrap_or_else(|| "Wav16".to_string());
116
+ let fmt_low = format_str.to_lowercase();
117
+
118
+ match fmt_low.as_str() {
119
+ "wav16" => {
120
+ let spec = hound::WavSpec {
121
+ channels: crate::core::audio::engine::CHANNELS,
122
+ sample_rate: sr,
123
+ bits_per_sample: 16,
124
+ sample_format: hound::SampleFormat::Int,
125
+ };
126
+
127
+ let mut writer = hound::WavWriter::create(output_dir, spec)
128
+ .map_err(|e| format!("Error creating WAV file: {}", e))?;
129
+
130
+ for sample in buffer.iter() {
131
+ writer
132
+ .write_sample(*sample)
133
+ .map_err(|e| format!("Error writing sample: {:?}", e))?;
134
+ }
135
+
136
+ writer
137
+ .finalize()
138
+ .map_err(|e| format!("Error finalizing WAV: {:?}", e))?;
139
+ }
140
+ "wav24" | "wav32" => {
141
+ let bits = if fmt_low.contains("24") { 24 } else { 32 };
142
+ let spec = hound::WavSpec {
143
+ channels: crate::core::audio::engine::CHANNELS,
144
+ sample_rate: sr,
145
+ bits_per_sample: bits,
146
+ sample_format: hound::SampleFormat::Int,
147
+ };
148
+
149
+ let mut writer = hound::WavWriter::create(output_dir, spec)
150
+ .map_err(|e| format!("Error creating WAV file: {}", e))?;
151
+
152
+ for &s in buffer.iter() {
153
+ let v32 = (s as i32) << (bits - 16);
154
+ writer
155
+ .write_sample(v32)
156
+ .map_err(|e| format!("Error writing sample: {:?}", e))?;
157
+ }
158
+
159
+ writer
160
+ .finalize()
161
+ .map_err(|e| format!("Error finalizing WAV: {:?}", e))?;
162
+ }
163
+ _ => {
164
+ return Err(format!("Unsupported audio format: {}", format_str));
165
+ }
166
+ }
167
+
168
+ Ok(())
169
+ }
@@ -1,56 +1,56 @@
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
- }
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
+ }
@@ -1,85 +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
- ) {
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
- }
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
+ }