1#![allow(non_camel_case_types)]
17
18use std::ffi::{c_int, c_long, c_uint, c_void};
19use std::sync::Once;
20
21pub const CODEC_IDX_AUDIO: c_uint = 0;
23pub const CODEC_IDX_VOICE: c_uint = 1;
24
25pub const DSP_RESET: c_uint = 0;
27pub const DSP_SET_FREQUENCY: c_uint = 1;
28pub const DSP_SET_SAMPLE_DEPTH: c_uint = 2;
29pub const DSP_SET_STEREO_MODE: c_uint = 3;
30pub const DSP_FLUSH: c_uint = 4;
31pub const DSP_SET_PITCH: c_uint = 5;
32pub const DSP_SET_OUT_FREQUENCY: c_uint = 6;
33pub const DSP_GET_OUT_FREQUENCY: c_uint = 7;
34
35pub const STEREO_INTERLEAVED: isize = 0;
37pub const STEREO_NONINTERLEAVED: isize = 1;
38pub const STEREO_MONO: isize = 2;
39
40pub const EQ_NUM_BANDS: usize = 10;
41
42pub const REPLAYGAIN_TRACK: c_int = 0;
44pub const REPLAYGAIN_ALBUM: c_int = 1;
45pub const REPLAYGAIN_SHUFFLE: c_int = 2; pub const REPLAYGAIN_OFF: c_int = 3;
47
48#[repr(C)]
50#[derive(Clone, Copy, Default)]
51pub struct sample_format {
52 pub version: u8,
53 pub num_channels: u8,
54 pub frac_bits: u8,
55 pub output_scale: u8,
56 pub frequency: i32,
57 pub codec_frequency: i32,
58}
59
60#[repr(C)]
61#[derive(Clone, Copy)]
62pub union dsp_buffer_ptrs {
63 pub pin: [*const c_void; 2],
64 pub p32: [*mut i32; 2],
65 pub p16out: *mut i16,
66}
67
68#[repr(C)]
69#[derive(Clone, Copy)]
70pub union dsp_buffer_count {
71 pub proc_mask: u32,
72 pub bufcount: c_int,
73}
74
75#[repr(C)]
77pub struct dsp_buffer {
78 pub remcount: i32,
79 pub ptrs: dsp_buffer_ptrs,
80 pub count: dsp_buffer_count,
81 pub format: sample_format,
82}
83
84#[repr(C)]
86#[derive(Clone, Copy)]
87pub struct eq_band_setting {
88 pub cutoff: c_int,
89 pub q: c_int,
90 pub gain: c_int,
91}
92
93#[repr(C)]
97#[derive(Clone, Copy, Default)]
98pub struct compressor_settings {
99 pub threshold: c_int,
100 pub makeup_gain: c_int,
101 pub ratio: c_int,
102 pub knee: c_int,
103 pub release_time: c_int,
104 pub attack_time: c_int,
105}
106
107#[repr(C)]
111#[derive(Clone, Copy)]
112pub struct replaygain_settings {
113 pub noclip: bool,
114 pub type_: c_int,
115 pub preamp: c_int,
116}
117
118pub const SOUND_CHAN_STEREO: c_int = 0;
120pub const SOUND_CHAN_MONO: c_int = 1;
121pub const SOUND_CHAN_CUSTOM: c_int = 2;
122pub const SOUND_CHAN_MONO_LEFT: c_int = 3;
123pub const SOUND_CHAN_MONO_RIGHT: c_int = 4;
124pub const SOUND_CHAN_KARAOKE: c_int = 5;
125pub const SOUND_CHAN_SWAP: c_int = 6;
126
127#[repr(C)]
129pub struct dsp_config {
130 _private: [u8; 0],
131}
132
133extern "C" {
134 pub fn dsp_init();
135 pub fn dsp_get_config(dsp_id: c_uint) -> *mut dsp_config;
136 pub fn dsp_get_id(dsp: *const dsp_config) -> c_uint;
137 pub fn dsp_configure(dsp: *mut dsp_config, setting: c_uint, value: isize) -> isize;
138 pub fn dsp_process(
139 dsp: *mut dsp_config,
140 src: *mut dsp_buffer,
141 dst: *mut dsp_buffer,
142 thread_yield: bool,
143 );
144
145 pub fn dsp_eq_enable(enable: bool);
147 pub fn dsp_set_eq_precut(precut: c_int);
148 pub fn dsp_set_eq_coefs(band: c_int, setting: *const eq_band_setting);
149
150 pub fn dsp_dither_enable(enable: bool);
152
153 pub fn tone_set_bass(bass: c_int);
156 pub fn tone_set_treble(treble: c_int);
157 pub fn tone_set_bass_cutoff(hz: c_int);
158 pub fn tone_set_treble_cutoff(hz: c_int);
159 pub fn tone_set_prescale(prescale: c_int);
160
161 pub fn dsp_surround_enable(delay_ms: c_int);
163 pub fn dsp_surround_set_balance(balance: c_int);
164 pub fn dsp_surround_set_cutoff(frq_l: c_int, frq_h: c_int);
165 pub fn dsp_surround_side_only(side_only: bool);
166 pub fn dsp_surround_mix(mix: c_int);
167
168 pub fn channel_mode_set_config(value: c_int);
170 pub fn channel_mode_custom_set_width(value: c_int);
171
172 pub fn dsp_set_compressor(settings: *const compressor_settings);
174
175 pub fn dsp_replaygain_set_settings(settings: *const replaygain_settings);
177 pub fn dsp_set_pitch(pitch: i32);
178 pub fn dsp_get_pitch() -> i32;
179 pub fn dsp_set_all_output_frequency(samplerate: c_uint);
180 pub fn dsp_get_output_frequency(dsp: *mut dsp_config) -> c_uint;
181
182 pub fn rbdsp_replaygain_set_gains(
186 track_gain: c_long,
187 album_gain: c_long,
188 track_peak: c_long,
189 album_peak: c_long,
190 );
191 pub fn get_replaygain_int(int_gain: c_long) -> c_long;
193}
194
195static DSP_INIT: Once = Once::new();
196
197pub struct Dsp {
202 cfg: *mut dsp_config,
203}
204
205impl Dsp {
206 pub fn new(sample_rate: u32) -> Self {
209 DSP_INIT.call_once(|| unsafe { dsp_init() });
210 let cfg = unsafe { dsp_get_config(CODEC_IDX_AUDIO) };
211 assert!(!cfg.is_null());
212 unsafe {
213 dsp_configure(cfg, DSP_RESET, 0);
214 dsp_configure(cfg, DSP_SET_OUT_FREQUENCY, sample_rate as isize);
215 dsp_configure(cfg, DSP_SET_FREQUENCY, sample_rate as isize);
216 dsp_configure(cfg, DSP_SET_SAMPLE_DEPTH, 16);
217 dsp_configure(cfg, DSP_SET_STEREO_MODE, STEREO_INTERLEAVED);
218 }
219 Dsp { cfg }
220 }
221
222 pub fn raw(&mut self) -> *mut dsp_config {
223 self.cfg
224 }
225
226 pub fn set_input_frequency(&mut self, hz: u32) {
229 unsafe { dsp_configure(self.cfg, DSP_SET_FREQUENCY, hz as isize) };
230 }
231
232 pub fn flush(&mut self) {
234 unsafe { dsp_configure(self.cfg, DSP_FLUSH, 0) };
235 }
236
237 pub fn eq_enable(&mut self, enable: bool) {
238 unsafe { dsp_eq_enable(enable) };
239 }
240
241 pub fn set_tone(&mut self, bass_db: i32, treble_db: i32) {
247 let bass = bass_db * 10;
248 let treble = treble_db * 10;
249 unsafe {
250 tone_set_bass(bass);
251 tone_set_treble(treble);
252 tone_set_prescale(bass.max(treble).max(0));
253 }
254 }
255
256 pub fn set_tone_cutoffs(&mut self, bass_hz: i32, treble_hz: i32) {
260 unsafe {
261 tone_set_bass_cutoff(bass_hz);
262 tone_set_treble_cutoff(treble_hz);
263 }
264 }
265
266 pub fn set_surround(&mut self, delay_ms: i32, balance: i32, fx1: i32, fx2: i32) {
271 unsafe {
272 dsp_surround_set_balance(balance);
273 if fx1 > 0 && fx2 > 0 {
274 dsp_surround_set_cutoff(fx1, fx2);
275 }
276 dsp_surround_enable(delay_ms);
277 }
278 }
279
280 pub fn set_channel_config(&mut self, mode: i32) {
283 unsafe { channel_mode_set_config(mode) };
284 }
285
286 pub fn set_stereo_width(&mut self, percent: i32) {
289 unsafe { channel_mode_custom_set_width(percent) };
290 }
291
292 pub fn set_compressor(&mut self, settings: &compressor_settings) {
294 unsafe { dsp_set_compressor(settings) };
295 }
296
297 pub fn set_replaygain(&mut self, mode: i32, noclip: bool, preamp_db: f32) {
303 let settings = replaygain_settings {
304 noclip,
305 type_: mode,
306 preamp: (preamp_db * 10.0).round() as c_int,
307 };
308 unsafe { dsp_replaygain_set_settings(&settings) };
309 }
310
311 pub fn set_replaygain_gains(
317 &mut self,
318 track_gain_db: Option<f32>,
319 album_gain_db: Option<f32>,
320 track_peak: Option<f32>,
321 album_peak: Option<f32>,
322 ) {
323 let gain = |db: Option<f32>| {
324 db.map_or(0, |db| unsafe {
325 get_replaygain_int((db * 100.0).round() as c_long)
326 })
327 };
328 let peak = |p: Option<f32>| p.map_or(0, |p| (p * (1 << 24) as f32).round() as c_long);
329 unsafe {
330 rbdsp_replaygain_set_gains(
331 gain(track_gain_db),
332 gain(album_gain_db),
333 peak(track_peak),
334 peak(album_peak),
335 )
336 };
337 }
338
339 pub fn set_replaygain_gains_raw(
343 &mut self,
344 track_gain: c_long,
345 album_gain: c_long,
346 track_peak: c_long,
347 album_peak: c_long,
348 ) {
349 unsafe { rbdsp_replaygain_set_gains(track_gain, album_gain, track_peak, album_peak) };
350 }
351
352 pub fn set_eq_band(&mut self, band: usize, cutoff_hz: i32, q: f32, gain_db: f32) {
356 assert!(band < EQ_NUM_BANDS);
357 let setting = eq_band_setting {
358 cutoff: cutoff_hz,
359 q: (q * 10.0).round() as c_int,
360 gain: (gain_db * 10.0).round() as c_int,
361 };
362 unsafe { dsp_set_eq_coefs(band as c_int, &setting) };
363 }
364
365 pub fn set_eq_band_raw(&mut self, band: usize, setting: eq_band_setting) {
369 assert!(band < EQ_NUM_BANDS);
370 unsafe { dsp_set_eq_coefs(band as c_int, &setting) };
371 }
372
373 pub fn set_eq_precut(&mut self, db: f32) {
375 unsafe { dsp_set_eq_precut((db * 10.0).round() as c_int) };
376 }
377
378 pub fn process(&mut self, input: &[i16], out: &mut Vec<i16>) -> usize {
383 assert!(input.len() % 2 == 0, "input must be interleaved stereo");
384 let mut produced = 0usize;
385 let mut chunk = [0i16; 8192]; let mut src = dsp_buffer {
388 remcount: (input.len() / 2) as i32,
389 ptrs: dsp_buffer_ptrs {
390 pin: [input.as_ptr() as *const c_void; 2],
391 },
392 count: dsp_buffer_count { proc_mask: 0 },
393 format: sample_format::default(), };
395
396 loop {
397 let mut dst = dsp_buffer {
398 remcount: 0,
399 ptrs: dsp_buffer_ptrs {
400 p16out: chunk.as_mut_ptr(),
401 },
402 count: dsp_buffer_count {
403 bufcount: (chunk.len() / 2) as c_int,
404 },
405 format: sample_format::default(),
406 };
407 unsafe { dsp_process(self.cfg, &mut src, &mut dst, false) };
408 let frames = dst.remcount as usize;
409 if frames == 0 && src.remcount <= 0 {
410 break;
411 }
412 out.extend_from_slice(&chunk[..frames * 2]);
413 produced += frames;
414 if src.remcount <= 0 && frames < chunk.len() / 2 {
415 break; }
417 }
418 produced
419 }
420}
421
422#[cfg(test)]
423mod tests {
424 use super::*;
425
426 #[test]
429 fn replaygain_track_gain_scales_amplitude() {
430 let mut dsp = Dsp::new(44100);
431 dsp.set_replaygain(REPLAYGAIN_TRACK, false, 0.0);
432 dsp.set_replaygain_gains(Some(-6.0206), None, None, None); let input: Vec<i16> = (0..44100)
435 .flat_map(|i| {
436 let s = ((i as f32 * 2.0 * std::f32::consts::PI * 1000.0 / 44100.0).sin() * 16000.0)
437 as i16;
438 [s, s]
439 })
440 .collect();
441 let mut out = Vec::new();
442 dsp.process(&input, &mut out);
443
444 let peak = out.iter().map(|s| (*s as i32).abs()).max().unwrap();
445 assert!(
446 (7600..=8400).contains(&peak),
447 "expected ~8000 after -6.02 dB track gain, got peak {peak}"
448 );
449
450 dsp.set_replaygain(REPLAYGAIN_OFF, false, 0.0);
452 dsp.flush();
453 out.clear();
454 dsp.process(&input, &mut out);
455 let peak = out.iter().map(|s| (*s as i32).abs()).max().unwrap();
456 assert!(
457 (15200..=16000).contains(&peak),
458 "expected ~16000 with replaygain off, got peak {peak}"
459 );
460 }
461}