Add equalizer band control for Cloud III S Wireless

- Add can_set_equalizer capability flag to DeviceState
- Add set_equalizer_band_packet trait method with default None implementation
- Add can_set_equalizer() helper method to Device trait
- Implement equalizer protocol: 10 bands (32Hz-16kHz), ±12dB range
- Encode values as dB*100 in 16-bit big-endian signed format
- Add Cloud III S Wireless implementation of set_equalizer_band_packet()
- Validate band index (0-9) and clamp dB values to valid range

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
NubeBuster
2026-01-17 04:26:11 +01:00
parent 0e46295ffd
commit 66dae28cf0
2 changed files with 41 additions and 0 deletions

View File

@@ -25,6 +25,14 @@ const AUTO_SHUTDOWN_REPORT_ID: u8 = 0x0c;
const AUTO_SHUTDOWN_CMD: [u8; 5] = [0x02, 0x03, 0x00, 0x00, 0x4a];
const AUTO_SHUTDOWN_PACKET_SIZE: usize = 64;
// Equalizer control (via SET_REPORT, report ID 0x0c)
// Packet structure: 0c 02 03 00 00 5f [band] [value_hi] [value_lo] 00... (64 bytes total)
// band: 0-9 (32Hz, 64Hz, 125Hz, 250Hz, 500Hz, 1kHz, 2kHz, 4kHz, 8kHz, 16kHz)
// value: signed 16-bit big-endian, dB * 100 (e.g., +6.0dB = 600, -3.5dB = -350)
const EQ_REPORT_ID: u8 = 0x0c;
const EQ_CMD: [u8; 5] = [0x02, 0x03, 0x00, 0x00, 0x5f];
const EQ_PACKET_SIZE: usize = 64;
// Button report header (incoming from headset)
const CONSUMER_CONTROL_HEADER: u8 = 0x0f;
// Consumer control button values
@@ -50,6 +58,19 @@ fn make_auto_shutdown_packet(minutes: u64) -> Vec<u8> {
packet
}
fn make_equalizer_band_packet(band_index: u8, db_value: f32) -> Vec<u8> {
let mut packet = vec![0u8; EQ_PACKET_SIZE];
packet[0] = EQ_REPORT_ID;
packet[1..6].copy_from_slice(&EQ_CMD);
packet[6] = band_index;
// Convert dB to device units (dB * 100), clamp to ±12dB range
let value_int = (db_value * 100.0).clamp(-1200.0, 1200.0) as i16;
let value_bytes = value_int.to_be_bytes();
packet[7] = value_bytes[0]; // High byte
packet[8] = value_bytes[1]; // Low byte
packet
}
pub struct CloudIIISWireless {
state: DeviceState,
}
@@ -162,6 +183,14 @@ impl Device for CloudIIISWireless {
None
}
// Cloud III S: Equalizer control - CONFIRMED WORKING
fn set_equalizer_band_packet(&self, band_index: u8, db_value: f32) -> Option<Vec<u8>> {
if band_index > 9 {
return None;
}
Some(make_equalizer_band_packet(band_index, db_value))
}
fn get_event_from_device_response(&self, response: &[u8]) -> Option<Vec<DeviceEvent>> {
debug_println!("Read packet: {response:?}");

View File

@@ -90,6 +90,7 @@ pub struct DeviceState {
pub can_set_side_tone_volume: bool,
pub can_set_voice_prompt: bool,
pub can_set_silent_mode: bool,
pub can_set_equalizer: bool,
}
impl Display for DeviceState {
@@ -145,6 +146,7 @@ impl DeviceState {
can_set_side_tone_volume: false,
can_set_voice_prompt: false,
can_set_silent_mode: false,
can_set_equalizer: false,
})
}
@@ -421,6 +423,11 @@ pub trait Device {
fn reset_sirk_packet(&self) -> Option<Vec<u8>>;
fn get_silent_mode_packet(&self) -> Option<Vec<u8>>;
fn set_silent_mode_packet(&self, silence: bool) -> Option<Vec<u8>>;
/// Set equalizer band (0-9) to dB value (-12.0 to +12.0)
/// Bands: 0=32Hz, 1=64Hz, 2=125Hz, 3=250Hz, 4=500Hz, 5=1kHz, 6=2kHz, 7=4kHz, 8=8kHz, 9=16kHz
fn set_equalizer_band_packet(&self, _band_index: u8, _db_value: f32) -> Option<Vec<u8>> {
None
}
fn get_event_from_device_response(&self, response: &[u8]) -> Option<Vec<DeviceEvent>>;
fn get_device_state(&self) -> &DeviceState;
fn get_device_state_mut(&mut self) -> &mut DeviceState;
@@ -451,6 +458,9 @@ pub trait Device {
fn can_set_silent_mode(&self) -> bool {
self.set_silent_mode_packet(false).is_some()
}
fn can_set_equalizer(&self) -> bool {
self.set_equalizer_band_packet(0, 0.0).is_some()
}
// Initialize capability flags in device state
fn init_capabilities(&mut self) {
@@ -462,6 +472,7 @@ pub trait Device {
let can_set_side_tone_volume = self.can_set_side_tone_volume();
let can_set_voice_prompt = self.can_set_voice_prompt();
let can_set_silent_mode = self.can_set_silent_mode();
let can_set_equalizer = self.can_set_equalizer();
// Now set them in device state
let state = self.get_device_state_mut();
@@ -472,6 +483,7 @@ pub trait Device {
state.can_set_side_tone_volume = can_set_side_tone_volume;
state.can_set_voice_prompt = can_set_voice_prompt;
state.can_set_silent_mode = can_set_silent_mode;
state.can_set_equalizer = can_set_equalizer;
}
fn execute_headset_specific_functionality(&mut self) -> Result<(), DeviceError> {