1use embedded_dsp::*;
13
14fn main() {
15 println!("===============================================================================");
16 println!(" embedded-dsp Audio & Speech Processing Pipeline ");
17 println!("===============================================================================");
18 println!();
19
20 const FS: f32 = 16000.0; const NUM_SAMPLES: usize = 1024;
22
23 println!("--- 1. Generating Audio Signal with Mains Hum and DC Offset ---");
27 let mut raw_audio = [0.0f32; NUM_SAMPLES];
28 let dc_bias = 0.35f32;
29 let hum_freq = 60.0f32;
30 let voice_f0 = 440.0f32; let voice_f1 = 880.0f32; for (i, sample) in raw_audio.iter_mut().enumerate() {
34 let t = i as f32 / FS;
35 let hum = 0.25 * (2.0 * core::f32::consts::PI * hum_freq * t).sin();
37 let voice = 0.5 * (2.0 * core::f32::consts::PI * voice_f0 * t).sin()
38 + 0.25 * (2.0 * core::f32::consts::PI * voice_f1 * t).sin();
39 let prng = ((i as u64).wrapping_mul(1103515245).wrapping_add(12345) % 2147483648) as f32
40 / 2147483648.0;
41 let noise = 0.05 * (prng - 0.5);
42 *sample = dc_bias + hum + voice + noise;
43 }
44
45 let mut mean_raw = 0.0f32;
46 let mut rms_raw = 0.0f32;
47 mean_f32(&raw_audio, &mut mean_raw);
48 rms_f32(&raw_audio, &mut rms_raw);
49 println!(
50 " Raw Signal: Mean (DC) = {:.4}, RMS = {:.4}",
51 mean_raw, rms_raw
52 );
53
54 println!("\n--- 2. DC Offset Removal (f32 & Q15 DC Blockers) ---");
58 let mut dc_blocked_f32 = [0.0f32; NUM_SAMPLES];
59 let mut dc_blocker = SinglePoleFilter::highpass(0.995);
61 for (i, &s) in raw_audio.iter().enumerate() {
62 dc_blocked_f32[i] = dc_blocker.process(s);
63 }
64
65 let mut mean_blocked = 0.0f32;
66 mean_f32(&dc_blocked_f32[512..], &mut mean_blocked);
68 println!(
69 " f32 Filter: Settled Mean after DC blocker = {:.6}",
70 mean_blocked
71 );
72
73 let mut dc_blocker_q15 = DcBlockerQ15::from_f32_decay(0.995);
75 let mut q15_raw = [q15::ZERO; NUM_SAMPLES];
76 f32_to_q15(&raw_audio, &mut q15_raw);
77 let mut q15_blocked = [q15::ZERO; NUM_SAMPLES];
78 for (i, &s) in q15_raw.iter().enumerate() {
79 q15_blocked[i] = dc_blocker_q15.process(s);
80 }
81 let mut mean_q15_out = q15::ZERO;
82 mean_q15(&q15_blocked[512..], &mut mean_q15_out);
83 println!(
84 " Q15 Filter: Settled Mean in Q15 = {} (expected ~ 0)",
85 mean_q15_out
86 );
87
88 println!("\n--- 3. Biquad Equalizer Cascade: 60 Hz Hum Rejection & 440 Hz Peaking ---");
92 let notch_coeffs = biquad_notch_coeffs(60.0, FS, 10.0);
94 let peaking_coeffs = biquad_peaking_coeffs(440.0, FS, 2.0, 6.0);
96
97 let mut cascade_coeffs = [0.0f32; 10];
98 cascade_coeffs[..5].copy_from_slice(¬ch_coeffs);
99 cascade_coeffs[5..].copy_from_slice(&peaking_coeffs);
100
101 let mut eq_state = [0.0f32; 4 * 2]; let mut eq_cascade = BiquadCascadeInstanceF32 {
103 num_stages: 2,
104 coeffs: &cascade_coeffs,
105 state: &mut eq_state,
106 };
107
108 let mut equalized_audio = [0.0f32; NUM_SAMPLES];
109 biquad_cascade_df1_f32(&mut eq_cascade, &dc_blocked_f32, &mut equalized_audio);
110
111 println!(
112 " Notch Filter Coeffs: b0={:.4}, b1={:.4}, b2={:.4}, a1={:.4}, a2={:.4}",
113 notch_coeffs[0], notch_coeffs[1], notch_coeffs[2], notch_coeffs[3], notch_coeffs[4]
114 );
115 println!(
116 " Peaking Filter Coeffs: b0={:.4}, b1={:.4}, b2={:.4}, a1={:.4}, a2={:.4}",
117 peaking_coeffs[0],
118 peaking_coeffs[1],
119 peaking_coeffs[2],
120 peaking_coeffs[3],
121 peaking_coeffs[4]
122 );
123 println!(
124 " EQ Cascade filtered {} samples successfully.",
125 equalized_audio.len()
126 );
127
128 println!("\n--- 4. Dynamics Envelope Tracking (Peak and RMS Followers) ---");
132 let mut peak_follower = PeakEnvelopeFollower::new(80.0, 800.0);
134 let mut rms_follower = RmsEnvelopeFollower::new(160.0);
135
136 let mut peak_env = [0.0f32; NUM_SAMPLES];
137 let mut rms_env = [0.0f32; NUM_SAMPLES];
138
139 for i in 0..NUM_SAMPLES {
140 peak_env[i] = peak_follower.process(equalized_audio[i]);
141 rms_env[i] = rms_follower.process(equalized_audio[i]);
142 }
143
144 println!(" Settled Peak Envelope: {:.4}", peak_env[NUM_SAMPLES - 1]);
145 println!(" Settled RMS Envelope : {:.4}", rms_env[NUM_SAMPLES - 1]);
146
147 let mut peak_follower_q15 = PeakEnvelopeFollowerQ15::new(80.0, 800.0);
149 let mut q15_eq = [q15::ZERO; NUM_SAMPLES];
150 f32_to_q15(&equalized_audio, &mut q15_eq);
151 let mut last_q15_peak = q15::ZERO;
152 for &sample in &q15_eq {
153 last_q15_peak = peak_follower_q15.process(sample);
154 }
155 println!(" Q15 Peak Envelope Level: {} (Q15)", last_q15_peak);
156
157 println!("\n--- 5. ITU-T G.711 Telecom Companding Codec (16-bit PCM -> 8-bit Byte) ---");
161 let test_samples: [i16; 6] = [0, 100, -500, 4096, -16384, 30000];
162 println!(
163 " {:<12} {:<12} {:<12} {:<12} {:<12}",
164 "Original PCM", "μ-law Byte", "μ-law Recv", "A-law Byte", "A-law Recv"
165 );
166 println!(" ------------------------------------------------------------------");
167 for &orig in &test_samples {
168 let u_byte = linear_to_ulaw(orig);
169 let u_dec = ulaw_to_linear(u_byte);
170 let a_byte = linear_to_alaw(orig);
171 let a_dec = alaw_to_linear(a_byte);
172 println!(
173 " {:<12} 0x{:02X} ({:<5}) {:<12} 0x{:02X} ({:<5}) {:<12}",
174 orig, u_byte, u_byte, u_dec, a_byte, a_byte, a_dec
175 );
176 }
177
178 let x_f32 = 0.15f32;
180 let u_comp = mu_law_compress_f32(x_f32);
181 let u_exp = mu_law_expand_f32(u_comp);
182 let a_comp = a_law_compress_f32(x_f32);
183 let a_exp = a_law_expand_f32(a_comp);
184 println!(
185 " Floating-point μ-law roundtrip: x = {:.4} -> comp = {:.4} -> expand = {:.4}",
186 x_f32, u_comp, u_exp
187 );
188 println!(
189 " Floating-point A-law roundtrip: x = {:.4} -> comp = {:.4} -> expand = {:.4}",
190 x_f32, a_comp, a_exp
191 );
192
193 println!("\n--- 6. Goertzel Dual-Tone Multi-Frequency (DTMF) Detection ---");
197 let dtmf_low_freq = 852.0f32;
199 let dtmf_high_freq = 1477.0f32;
200 let mut dtmf_signal = [0.0f32; 256];
201
202 for (i, s) in dtmf_signal.iter_mut().enumerate() {
203 let t = i as f32 / FS;
204 *s = 0.5 * (2.0 * core::f32::consts::PI * dtmf_low_freq * t).sin()
205 + 0.5 * (2.0 * core::f32::consts::PI * dtmf_high_freq * t).sin();
206 }
207
208 let candidate_freqs = [
210 697.0f32, 770.0, 852.0, 941.0, 1209.0, 1336.0, 1477.0, 1633.0,
211 ];
212 println!(" Scanning 8 DTMF frequency bins on 256-sample frame:");
213
214 for &freq in &candidate_freqs {
215 let mut detector = GoertzelDetector::new(freq, FS);
216 for &sample in &dtmf_signal {
217 detector.process_sample(sample);
218 }
219 let mag = detector.magnitude();
220 let is_detected = mag > 0.35;
221 let star = if is_detected {
222 " <== DETECTED TONE"
223 } else {
224 ""
225 };
226 println!(
227 " Frequency {:>6.1} Hz: Magnitude = {:.4}{}",
228 freq, mag, star
229 );
230 }
231
232 println!("\n--- 7. MFCC Speech Feature Extraction (Acoustic Frontend) ---");
236 const FRAME_SIZE: usize = 256;
238 let mut speech_frame = [0.0f32; FRAME_SIZE];
239 for (i, val) in speech_frame.iter_mut().enumerate() {
240 let t = i as f32 / FS;
241 *val = 0.6 * (2.0 * core::f32::consts::PI * 500.0 * t).sin()
243 + 0.3 * (2.0 * core::f32::consts::PI * 1500.0 * t).sin()
244 + 0.1 * (2.0 * core::f32::consts::PI * 2500.0 * t).sin();
245 }
246
247 let mut window = [0.0f32; FRAME_SIZE];
249 hamming_f32(&mut window);
250 apply_window_f32(&mut speech_frame, &window);
251
252 let mut mel_scratch = [0.0f32; 26];
254 let mut mfcc_coeffs = [0.0f32; 13];
255
256 let status = mfcc_f32(
257 &speech_frame,
258 FS,
259 100.0, 7000.0, &mut mel_scratch,
262 &mut mfcc_coeffs,
263 );
264
265 if status == Status::Success {
266 println!(" Extracted 13 MFCC Coefficients for Wake-Word / Speech AI:");
267 for (idx, coeff) in mfcc_coeffs.iter().enumerate() {
268 println!(" MFCC[{:>2}]: {:>9.4}", idx, coeff);
269 }
270 } else {
271 println!(" MFCC extraction failed with status: {:?}", status);
272 }
273
274 println!();
275 println!("===============================================================================");
276 println!(" Audio & Speech Pipeline Execution Complete! ");
277 println!("===============================================================================");
278}