1use crate::core::{Block, WorkReport};
5use crate::dsp::{Nco, mix_with_nco};
6use num_complex::Complex32 as C32;
7
8#[derive(Debug, Clone)]
10pub struct CwKeyedMod {
11 nco: Nco,
12 env: f32,
13 alpha_rise: f32,
14 alpha_fall: f32,
15 gain: f32,
16}
17
18impl CwKeyedMod {
19 pub fn new(sample_rate: f32, tone_hz: f32, rise_ms: f32, fall_ms: f32) -> Self {
22 let tau_r = (rise_ms.max(0.1) * 1e-3) * sample_rate;
23 let tau_f = (fall_ms.max(0.1) * 1e-3) * sample_rate;
24 let alpha_r = (-1.0 / tau_r).exp();
25 let alpha_f = (-1.0 / tau_f).exp();
26 Self {
27 nco: Nco::new(tone_hz, sample_rate),
28 env: 0.0,
29 alpha_rise: alpha_r,
30 alpha_fall: alpha_f,
31 gain: 1.0,
32 }
33 }
34 pub fn set_gain(&mut self, g: f32) {
35 self.gain = g;
36 }
37}
38
39impl Block for CwKeyedMod {
40 type In = f32; type Out = C32; #[inline(always)]
44 fn process(&mut self, input: &[f32], output: &mut [C32]) -> WorkReport {
45 let n = input.len().min(output.len());
46 let mut i = 0;
47 let nn = n & !3;
48
49 while i < nn {
50 let tgt0 = input[i].clamp(0.0, 1.0);
52 self.env = if tgt0 >= self.env {
53 self.alpha_rise * self.env + (1.0 - self.alpha_rise) * tgt0
54 } else {
55 self.alpha_fall * self.env + (1.0 - self.alpha_fall) * tgt0
56 };
57 output[i] = mix_with_nco(C32::new(self.env * self.gain, 0.0), &mut self.nco);
58
59 let tgt1 = input[i + 1].clamp(0.0, 1.0);
61 self.env = if tgt1 >= self.env {
62 self.alpha_rise * self.env + (1.0 - self.alpha_rise) * tgt1
63 } else {
64 self.alpha_fall * self.env + (1.0 - self.alpha_fall) * tgt1
65 };
66 output[i + 1] = mix_with_nco(C32::new(self.env * self.gain, 0.0), &mut self.nco);
67
68 let tgt2 = input[i + 2].clamp(0.0, 1.0);
70 self.env = if tgt2 >= self.env {
71 self.alpha_rise * self.env + (1.0 - self.alpha_rise) * tgt2
72 } else {
73 self.alpha_fall * self.env + (1.0 - self.alpha_fall) * tgt2
74 };
75 output[i + 2] = mix_with_nco(C32::new(self.env * self.gain, 0.0), &mut self.nco);
76
77 let tgt3 = input[i + 3].clamp(0.0, 1.0);
79 self.env = if tgt3 >= self.env {
80 self.alpha_rise * self.env + (1.0 - self.alpha_rise) * tgt3
81 } else {
82 self.alpha_fall * self.env + (1.0 - self.alpha_fall) * tgt3
83 };
84 output[i + 3] = mix_with_nco(C32::new(self.env * self.gain, 0.0), &mut self.nco);
85
86 i += 4;
87 }
88 while i < n {
89 let tgt = input[i].clamp(0.0, 1.0);
90 self.env = if tgt >= self.env {
91 self.alpha_rise * self.env + (1.0 - self.alpha_rise) * tgt
92 } else {
93 self.alpha_fall * self.env + (1.0 - self.alpha_fall) * tgt
94 };
95 output[i] = mix_with_nco(C32::new(self.env * self.gain, 0.0), &mut self.nco);
96 i += 1;
97 }
98 WorkReport {
99 in_read: n,
100 out_written: n,
101 }
102 }
103}