1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
use crate::device::PD200X;
use crate::error::Result;
use crate::protocol::{cmd, EqBand, EqBandParams, RgbColor, encode_nc_strength};
impl PD200X {
// ---- Microphone ----
/// Mute or unmute the microphone.
///
/// When muted the mic input is silenced at the firmware level regardless
/// of the gain setting.
pub async fn set_mute(&self, muted: bool) -> Result<()> {
self.send_command(cmd::MIC_MUTE, muted as u16).await?;
Ok(())
}
/// Returns `true` if the microphone is currently muted.
pub async fn get_mute(&self) -> Result<bool> {
Ok(self.query(cmd::MIC_MUTE).await? != 0)
}
/// Set the microphone input gain (0–100).
pub async fn set_mic_gain(&self, gain: u8) -> Result<()> {
self.send_command(cmd::MIC_GAIN, gain as u16).await?;
Ok(())
}
/// Returns the current microphone input gain (0–100).
pub async fn get_mic_gain(&self) -> Result<u8> {
Ok(self.query(cmd::MIC_GAIN).await? as u8)
}
/// Enable or disable microphone monitoring.
///
/// When enabled, the microphone input is routed directly to the headphone
/// output with near-zero latency so you can hear yourself while speaking.
///
/// Internally this writes two registers (`0x2031` and `0x2070`) with a
/// short delay between them to give the device time to apply both settings.
pub async fn set_mic_monitor(&self, enabled: bool) -> Result<()> {
self.send_command(cmd::NOISE_GATE, if enabled { 2000 } else { 3000 }).await?;
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
self.send_command(cmd::MIC_MONITOR, !enabled as u16).await?;
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
Ok(())
}
/// Returns `true` if microphone monitoring is currently enabled.
pub async fn get_mic_monitor(&self) -> Result<bool> {
Ok(self.query(cmd::MIC_MONITOR).await? == 0)
}
// ---- Audio ----
/// Set the headphone output volume (0–100).
pub async fn set_headphone_volume(&self, volume: u8) -> Result<()> {
self.send_command(cmd::HEADPHONE_VOL, volume as u16).await?;
Ok(())
}
/// Returns the current headphone output volume (0–100).
pub async fn get_headphone_volume(&self) -> Result<u8> {
Ok(self.query(cmd::HEADPHONE_VOL).await? as u8)
}
/// Set the noise gate threshold (1200–3000).
///
/// The noise gate silences the mic input when the signal falls below this
/// threshold. Higher values mean a more aggressive gate.
///
/// Note: this register is also written by [`set_mic_monitor`] —
/// calling `set_noise_gate` while monitoring is enabled will change the
/// threshold but leave monitoring unaffected.
///
/// [`set_mic_monitor`]: PD200X::set_mic_monitor
pub async fn set_noise_gate(&self, threshold: u16) -> Result<()> {
self.send_command(cmd::NOISE_GATE, threshold).await?;
Ok(())
}
/// Returns the current noise gate threshold (1200–3000).
pub async fn get_noise_gate(&self) -> Result<u16> {
self.query(cmd::NOISE_GATE).await
}
/// Enable or disable the AI noise cancellation.
pub async fn set_noise_cancellation(&self, enabled: bool) -> Result<()> {
self.send_command(cmd::NC_ENABLE, enabled as u16).await?;
Ok(())
}
/// Returns `true` if noise cancellation is currently enabled.
pub async fn get_noise_cancellation(&self) -> Result<bool> {
Ok(self.query(cmd::NC_ENABLE).await? != 0)
}
/// Set the noise cancellation strength (0–40).
///
/// Higher values apply stronger noise reduction. The value is encoded as
/// `(strength + 120) × 10` before being sent to the firmware.
pub async fn set_nc_strength(&self, strength: u8) -> Result<()> {
self.send_command(cmd::NC_STRENGTH, encode_nc_strength(strength)).await?;
Ok(())
}
/// Returns the current noise cancellation strength (0–40).
pub async fn get_nc_strength(&self) -> Result<u8> {
Ok((self.query(cmd::NC_STRENGTH).await? / 10).saturating_sub(120) as u8)
}
// ---- EQ ----
/// Configure a parametric EQ band.
///
/// All seven bands (`Band1`–`Band5`, `Hpf`, `Lpf`) are set in a single
/// USB packet. Changes take effect immediately.
///
/// For the `Hpf` and `Lpf` bands `gain_db` and `q` in [`EqBandParams`]
/// are sent as firmware constants; use [`set_hpf`] / [`set_lpf`] for a
/// simpler interface, or supply a full [`EqBandParams`] here for complete
/// control.
///
/// [`set_hpf`]: PD200X::set_hpf
/// [`set_lpf`]: PD200X::set_lpf
pub async fn set_eq_band(&self, band: EqBand, params: &EqBandParams) -> Result<()> {
self.send_eq(band, params).await
}
/// Enable or disable the high-pass filter at the given cutoff frequency (Hz).
///
/// Uses fixed Q = 1.0. For a custom Q use [`set_eq_band`] with
/// [`EqBand::Hpf`] directly.
///
/// [`set_eq_band`]: PD200X::set_eq_band
pub async fn set_hpf(&self, enabled: bool, frequency: u16) -> Result<()> {
use crate::protocol::FilterType;
self.send_eq(EqBand::Hpf, &EqBandParams {
enabled,
filter_type: FilterType::HighPass,
frequency,
gain_db: 0.0,
q: 1.0,
})
.await
}
/// Enable or disable the low-pass filter at the given cutoff frequency (Hz).
///
/// Uses fixed Q = 1.0. For a custom Q use [`set_eq_band`] with
/// [`EqBand::Lpf`] directly.
///
/// [`set_eq_band`]: PD200X::set_eq_band
pub async fn set_lpf(&self, enabled: bool, frequency: u16) -> Result<()> {
use crate::protocol::FilterType;
self.send_eq(EqBand::Lpf, &EqBandParams {
enabled,
filter_type: FilterType::LowPass,
frequency,
gain_db: 0.0,
q: 1.0,
})
.await
}
// ---- Compressor ----
/// Enable or disable the compressor and set its threshold.
///
/// The compressor reduces the dynamic range of the microphone signal.
/// `threshold` is a raw firmware value; higher values mean the compressor
/// activates at a louder input level.
pub async fn set_compressor(&self, enabled: bool, threshold: u16) -> Result<()> {
self.send_command(cmd::COMPRESSOR_ENABLE, enabled as u16).await?;
self.send_command(cmd::COMPRESSOR_THRESH, threshold).await?;
Ok(())
}
// ---- Limiter ----
/// Enable or disable the limiter and set its threshold.
///
/// The limiter hard-clips the output above `threshold` to prevent
/// clipping downstream. `threshold` is a raw firmware value.
pub async fn set_limiter(&self, enabled: bool, threshold: u16) -> Result<()> {
self.send_command(cmd::LIMITER_ENABLE, enabled as u16).await?;
self.send_command(cmd::LIMITER_THRESH, threshold).await?;
Ok(())
}
// ---- RGB Lighting ----
/// Turn the RGB LED strip on or off.
pub async fn set_rgb_enabled(&self, enabled: bool) -> Result<()> {
self.send_command(cmd::RGB_ENABLE, enabled as u16).await?;
Ok(())
}
/// Set the RGB LED strip brightness (0–100).
pub async fn set_rgb_brightness(&self, brightness: u8) -> Result<()> {
self.send_command(cmd::RGB_BRIGHTNESS, brightness as u16).await?;
Ok(())
}
/// Set the RGB LED strip colour.
pub async fn set_rgb_color(&self, color: RgbColor) -> Result<()> {
self.send_command(cmd::RGB_COLOR, color as u16).await?;
Ok(())
}
/// Link the RGB lighting to the mic activity (voice/mute state).
///
/// When enabled the LED colour reflects whether the mic is active.
pub async fn set_lighting_linkage(&self, enabled: bool) -> Result<()> {
self.send_command(cmd::LIGHTING_LINKAGE, enabled as u16).await?;
Ok(())
}
}