devalang_core/core/audio/engine/sample/
padding.rs1use devalang_types::Value;
2use std::collections::HashMap;
3
4pub fn pad_samples_impl(
5 engine: &mut crate::core::audio::engine::driver::AudioEngine,
6 samples: &[i16],
7 time_secs: f32,
8 effects_map: Option<HashMap<String, Value>>,
9) {
10 let sample_rate = engine.sample_rate as f32;
11 let channels = engine.channels as usize;
12
13 let offset = (time_secs * (sample_rate) * (channels as f32)) as usize;
14 let total_samples = samples.len();
15
16 let mut gain = 1.0;
17 let mut pan = 0.0;
18 let mut fade_in = 0.0;
19 let mut fade_out = 0.0;
20 let mut pitch = 1.0;
21 let mut drive = 0.0;
22 let mut reverb = 0.0;
23 let mut delay = 0.0; let delay_feedback = 0.35; if let Some(map) = &effects_map {
27 for (key, val) in map {
28 match (key.as_str(), val) {
29 ("gain", Value::Number(v)) => {
30 gain = *v;
31 }
32 ("pan", Value::Number(v)) => {
33 pan = *v;
34 }
35 ("fadeIn", Value::Number(v)) => {
36 fade_in = *v;
37 }
38 ("fadeOut", Value::Number(v)) => {
39 fade_out = *v;
40 }
41 ("pitch", Value::Number(v)) => {
42 pitch = *v;
43 }
44 ("drive", Value::Number(v)) => {
45 drive = *v;
46 }
47 ("reverb", Value::Number(v)) => {
48 reverb = *v;
49 }
50 ("delay", Value::Number(v)) => {
51 delay = *v;
52 }
53 _ => eprintln!("⚠️ Unknown or invalid effect '{}'", key),
54 }
55 }
56 }
57
58 let fade_in_samples = (fade_in * (sample_rate)) as usize;
59 let fade_out_samples = (fade_out * (sample_rate)) as usize;
60
61 let default_boundary_fade_ms = 1.0_f32; let default_fade_samples = (default_boundary_fade_ms * (sample_rate)) as usize;
64 let mut effective_fade_in = fade_in_samples;
65 let mut effective_fade_out = fade_out_samples;
66 if effective_fade_in == 0 {
67 if let Some(&first) = samples.first() {
68 if first.abs() > 64 {
69 effective_fade_in = default_fade_samples.max(1);
71 }
72 }
73 }
74 if effective_fade_out == 0 {
75 if let Some(&last) = samples.last() {
76 if last.abs() > 64 {
77 effective_fade_out = default_fade_samples.max(1);
79 }
80 }
81 }
82
83 if total_samples > 0 {
85 let cap = total_samples / 2;
86 if effective_fade_in > cap {
87 effective_fade_in = cap.max(1);
88 }
89 if effective_fade_out > cap {
90 effective_fade_out = cap.max(1);
91 }
92 }
93
94 let delay_samples = if delay > 0.0 {
95 (delay * (sample_rate)) as usize
96 } else {
97 0
98 };
99 let mut delay_buffer: Vec<f32> = vec![0.0; total_samples + delay_samples];
100
101 for i in 0..total_samples {
102 let pitch_index = if pitch != 1.0 {
103 ((i as f32) / pitch) as usize
104 } else {
105 i
106 };
107
108 let mut adjusted = if pitch_index < total_samples {
109 samples[pitch_index] as f32
110 } else {
111 0.0
112 };
113
114 adjusted *= gain;
115
116 if effective_fade_in > 0 && i < effective_fade_in {
117 if effective_fade_in == 1 {
118 adjusted *= 0.0;
119 } else {
120 adjusted *= (i as f32) / (effective_fade_in as f32);
121 }
122 }
123 if effective_fade_out > 0 && i >= total_samples.saturating_sub(effective_fade_out) {
124 if effective_fade_out == 1 {
125 adjusted *= 0.0;
126 } else {
127 adjusted *= ((total_samples - 1 - i) as f32) / ((effective_fade_out - 1) as f32);
128 }
129 }
130
131 if drive > 0.0 {
132 let normalized = adjusted / (i16::MAX as f32);
133 let pre_gain = (10f32).powf(drive / 20.0);
134 let driven = (normalized * pre_gain).tanh();
135 adjusted = driven * (i16::MAX as f32);
136 }
137
138 if delay_samples > 0 && i >= delay_samples {
139 let echo = delay_buffer[i - delay_samples] * delay_feedback;
140 adjusted += echo;
141 }
142 if delay_samples > 0 {
143 delay_buffer[i] = adjusted;
144 }
145
146 if reverb > 0.0 {
147 let reverb_delay = (0.03 * (sample_rate)) as usize;
148 if i >= reverb_delay {
149 adjusted += (engine.buffer[offset + i - reverb_delay] as f32) * reverb;
150 }
151 }
152
153 let adjusted_sample = adjusted.round().clamp(i16::MIN as f32, i16::MAX as f32) as i16;
154
155 let (left_gain, right_gain) = crate::core::audio::engine::helpers::pan_gains(pan);
156
157 let left = ((adjusted_sample as f32) * left_gain) as i16;
158 let right = ((adjusted_sample as f32) * right_gain) as i16;
159
160 let left_pos = offset + i * channels;
163 let right_pos = left_pos + 1;
164
165 if right_pos < engine.buffer.len() {
166 engine.buffer[left_pos] = engine.buffer[left_pos].saturating_add(left);
167 engine.buffer[right_pos] = engine.buffer[right_pos].saturating_add(right);
168 }
169 }
170}