Skip to content

Commit

Permalink
Add parametric equalizer and implement it for Arctis Nova 5
Browse files Browse the repository at this point in the history
  • Loading branch information
ne0phyte committed Feb 7, 2025
1 parent 343d071 commit e537f2f
Show file tree
Hide file tree
Showing 11 changed files with 579 additions and 136 deletions.
54 changes: 27 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,33 @@ talking. This differs from a simple loopback via PulseAudio as you won't have an

## Supported Headsets

| Device | sidetone | battery | notification sound | lights | inactive time | chatmix | voice prompts | rotate to mute | equalizer preset | equalizer | microphone mute led brightness | microphone volume | volume limiter | bluetooth when powered on | bluetooth call volume |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Corsair Headset Device | x | x | x | x | | | | | | | | | | | |
| HyperX Cloud Alpha Wireless | x | x | | | x | | x | | | | | | | | |
| HyperX Cloud Flight Wireless | | x | | | | | | | | | | | | | |
| HyperX Cloud 3 | x | | | | | | | | | | | | | | |
| Logitech G430 | x | | | | | | | | | | | | | | |
| Logitech G432/G433 | x | | | | | | | | | | | | | | |
| Logitech G533 | x | x | | | x | | | | | | | | | | |
| Logitech G535 | x | x | | | x | | | | | | | | | | |
| Logitech G930 | x | x | | | | | | | | | | | | | |
| Logitech G633/G635/G733/G933/G935 | x | x | | x | | | | | | | | | | | |
| Logitech G PRO Series | x | x | | | x | | | | | | | | | | |
| Logitech G PRO X 2 | x | | | | x | | | | | | | | | | |
| Logitech Zone Wired/Zone 750 | x | | | | | | x | x | | | | | | | |
| SteelSeries Arctis (1/7X/7P) Wireless | x | x | | | x | | | | | | | | | | |
| SteelSeries Arctis (7/Pro) | x | x | | x | x | x | | | | | | | | | |
| SteelSeries Arctis 9 | x | x | | | x | x | | | | | | | | | |
| SteelSeries Arctis Pro Wireless | x | x | | | x | | | | | | | | | | |
| ROCCAT Elo 7.1 Air | | | | x | x | | | | | | | | | | |
| ROCCAT Elo 7.1 USB | | | | x | | | | | | | | | | | |
| SteelSeries Arctis Nova 3 | x | | | | | | | | x | x | x | x | | | |
| SteelSeries Arctis Nova (5/5X) | x | x | | | x | x | | | x | x | x | x | x | | |
| SteelSeries Arctis Nova 7 | x | x | | | x | x | | | x | x | x | x | x | x | x |
| SteelSeries Arctis 7+ | x | x | | | x | x | | | x | x | | | | | |
| SteelSeries Arctis Nova Pro Wireless | x | x | | x | x | | | | x | x | | | | | |
| HeadsetControl Test device | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x |
| Device | sidetone | battery | notification sound | lights | inactive time | chatmix | voice prompts | rotate to mute | equalizer preset | equalizer | parametric equalizer | microphone mute led brightness | microphone volume | volume limiter | bluetooth when powered on | bluetooth call volume |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| Corsair Headset Device | x | x | x | x | | | | | | | | | | | | |
| HyperX Cloud Alpha Wireless | x | x | | | x | | x | | | | | | | | | |
| HyperX Cloud Flight Wireless | | x | | | | | | | | | | | | | | |
| HyperX Cloud 3 | x | | | | | | | | | | | | | | | |
| Logitech G430 | x | | | | | | | | | | | | | | | |
| Logitech G432/G433 | x | | | | | | | | | | | | | | | |
| Logitech G533 | x | x | | | x | | | | | | | | | | | |
| Logitech G535 | x | x | | | x | | | | | | | | | | | |
| Logitech G930 | x | x | | | | | | | | | | | | | | |
| Logitech G633/G635/G733/G933/G935 | x | x | | x | | | | | | | | | | | | |
| Logitech G PRO Series | x | x | | | x | | | | | | | | | | | |
| Logitech G PRO X 2 | x | | | | x | | | | | | | | | | | |
| Logitech Zone Wired/Zone 750 | x | | | | | | x | x | | | | | | | | |
| SteelSeries Arctis (1/7X/7P) Wireless | x | x | | | x | | | | | | | | | | | |
| SteelSeries Arctis (7/Pro) | x | x | | x | x | x | | | | | | | | | | |
| SteelSeries Arctis 9 | x | x | | | x | x | | | | | | | | | | |
| SteelSeries Arctis Pro Wireless | x | x | | | x | | | | | | | | | | | |
| ROCCAT Elo 7.1 Air | | | | x | x | | | | | | | | | | | |
| ROCCAT Elo 7.1 USB | | | | x | | | | | | | | | | | | |
| SteelSeries Arctis Nova 3 | x | | | | | | | | x | x | | x | x | | | |
| SteelSeries Arctis Nova (5/5X) | x | x | | | x | x | | | x | x | x | x | x | x | | |
| SteelSeries Arctis Nova 7 | x | x | | | x | x | | | x | x | | x | x | x | x | x |
| SteelSeries Arctis 7+ | x | x | | | x | x | | | x | x | | | | | | |
| SteelSeries Arctis Nova Pro Wireless | x | x | | x | x | | | | x | x | | | | | | |
| HeadsetControl Test device | x | x | x | x | x | x | x | x | x | x | | x | x | x | x | x |

For non-supported headsets on Linux: There is a chance that you can set the sidetone via AlsaMixer

Expand Down
13 changes: 13 additions & 0 deletions src/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const char* const capabilities_str[NUM_CAPABILITIES]
[CAP_ROTATE_TO_MUTE] = "rotate to mute",
[CAP_EQUALIZER_PRESET] = "equalizer preset",
[CAP_EQUALIZER] = "equalizer",
[CAP_PARAMETRIC_EQUALIZER] = "parametric equalizer",
[CAP_MICROPHONE_MUTE_LED_BRIGHTNESS] = "microphone mute led brightness",
[CAP_MICROPHONE_VOLUME] = "microphone volume",
[CAP_VOLUME_LIMITER] = "volume limiter",
Expand All @@ -31,6 +32,7 @@ const char* const capabilities_str_enum[NUM_CAPABILITIES]
[CAP_ROTATE_TO_MUTE] = "CAP_ROTATE_TO_MUTE",
[CAP_EQUALIZER_PRESET] = "CAP_EQUALIZER_PRESET",
[CAP_EQUALIZER] = "CAP_EQUALIZER",
[CAP_PARAMETRIC_EQUALIZER] = "CAP_PARAMETRIC_EQUALIZER",
[CAP_MICROPHONE_MUTE_LED_BRIGHTNESS] = "CAP_MICROPHONE_MUTE_LED_BRIGHTNESS",
[CAP_MICROPHONE_VOLUME] = "CAP_MICROPHONE_VOLUME",
[CAP_VOLUME_LIMITER] = "CAP_VOLUME_LIMITER",
Expand All @@ -50,10 +52,21 @@ const char capabilities_str_short[NUM_CAPABILITIES]
[CAP_ROTATE_TO_MUTE] = 'r',
[CAP_EQUALIZER_PRESET] = 'p',
[CAP_EQUALIZER] = 'e',
[CAP_PARAMETRIC_EQUALIZER] = 'q',
[CAP_MICROPHONE_MUTE_LED_BRIGHTNESS] = 't',
[CAP_MICROPHONE_VOLUME] = 'o',
// new capabilities since short output was deprecated
[CAP_VOLUME_LIMITER] = '\0',
[CAP_BT_WHEN_POWERED_ON] = '\0',
[CAP_BT_CALL_VOLUME] = '\0'
};

const char* const equalizer_filter_type_str[NUM_EQ_FILTER_TYPES]
= {
[EQ_FILTER_LOWSHELF] = "lowshelf",
[EQ_FILTER_LOWPASS] = "lowpass",
[EQ_FILTER_PEAKING] = "peaking",
[EQ_FILTER_HIGHPASS] = "highpass",
[EQ_FILTER_HIGHSHELF] = "highshelf"

};
68 changes: 66 additions & 2 deletions src/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ enum capabilities {
CAP_ROTATE_TO_MUTE,
CAP_EQUALIZER_PRESET,
CAP_EQUALIZER,
CAP_PARAMETRIC_EQUALIZER,
CAP_MICROPHONE_MUTE_LED_BRIGHTNESS,
CAP_MICROPHONE_VOLUME,
CAP_VOLUME_LIMITER,
Expand Down Expand Up @@ -103,6 +104,19 @@ typedef struct {
float* values;
} EqualizerPreset;

typedef struct {
int bands_count;
float gain_base; // default/base gain
float gain_step;
float gain_min;
float gain_max;
float q_factor_min; // q factor
float q_factor_max;
int freq_min; // frequency
int freq_max;
int filter_types; // bitmap containing available filter types
} ParametricEqualizerInfo;

typedef struct {
int count;
EqualizerPreset presets[];
Expand All @@ -112,6 +126,7 @@ enum headsetcontrol_errors {
HSC_ERROR = -100,
HSC_READ_TIMEOUT = -101,
HSC_OUT_OF_BOUNDS = -102,
HSC_INVALID_ARG = -103,
};

typedef enum {
Expand Down Expand Up @@ -140,7 +155,7 @@ typedef struct {
FeatureResult result;
} FeatureRequest;

/** @brief Defines equalizer custom setings
/** @brief Defines equalizer custom settings
*/
struct equalizer_settings {
/// The size of the bands array
Expand All @@ -149,6 +164,36 @@ struct equalizer_settings {
float* bands_values;
};

typedef enum {
EQ_FILTER_LOWSHELF,
EQ_FILTER_LOWPASS,
EQ_FILTER_PEAKING,
EQ_FILTER_HIGHPASS,
EQ_FILTER_HIGHSHELF,
NUM_EQ_FILTER_TYPES
} EqualizerFilterType;

/// Enum name of every parametric equalizer filter type
extern const char* const equalizer_filter_type_str[NUM_EQ_FILTER_TYPES];

/** @brief Defines parametric equalizer custom settings
*/
struct parametric_equalizer_settings {
/// The size of the bands array
int size;
/// The equalizer bands
struct parametric_equalizer_band* bands;
};

/** @brief Defines parameteric equalizer band
*/
struct parametric_equalizer_band {
float frequency;
float gain;
float q_factor;
EqualizerFilterType type;
};

/** @brief Defines the basic data of a device
*
* Also defines function pointers for using supported features
Expand All @@ -168,7 +213,8 @@ struct device {

// Equalizer Infos
EqualizerInfo* equalizer;
EqualizerPresets* eqaulizer_presets;
EqualizerPresets* equalizer_presets;
ParametricEqualizerInfo* parametric_equalizer;

wchar_t device_hid_vendorname[64];
wchar_t device_hid_productname[64];
Expand Down Expand Up @@ -318,6 +364,24 @@ struct device {
*/
int (*send_equalizer)(hid_device* hid_device, struct equalizer_settings* settings);

/** @brief Function pointer for setting headset parametric equalizer
*
* Forwards the request to the device specific implementation
*
* @param device_handle The hidapi handle. Must be the same
* device as defined here (same ids)
* @param settings The parametric equalizer settings containing
* the filter values
*
* @returns > 0 on success
* HSC_OUT_OF_BOUNDS on equalizer settings size out of range
* specific to this hardware
* HSC_INVALID_ARG on equalizer filter type invalid/unsupported
* specific to this hardware
* -1 HIDAPI error
*/
int (*send_parametric_equalizer)(hid_device* hid_device, struct parametric_equalizer_settings* settings);

/** @brief Function pointer for setting headset microphone mute LED brightness
*
* Forwards the request to the device specific implementation
Expand Down
11 changes: 9 additions & 2 deletions src/devices/headsetcontrol_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ static int headsetcontrol_test_notification_sound(hid_device* device_handle, uin
static int headsetcontrol_test_lights(hid_device* device_handle, uint8_t on);
static int headsetcontrol_test_send_equalizer_preset(hid_device* device_handle, uint8_t num);
static int headsetcontrol_test_send_equalizer(hid_device* device_handle, struct equalizer_settings* settings);
static int headsetcontrol_test_send_parametric_equalizer(hid_device* device_handle, struct parametric_equalizer_settings* settings);
static int headsetcontrol_test_send_microphone_mute_led_brightness(hid_device* device_handle, uint8_t num);
static int headsetcontrol_test_send_microphone_volume(hid_device* device_handle, uint8_t num);
static int headsetcontrol_test_switch_voice_prompts(hid_device* device_handle, uint8_t on);
Expand All @@ -55,15 +56,15 @@ void headsetcontrol_test_init(struct device** device)
device_headsetcontrol_test.idProductsSupported = PRODUCT_IDS;
device_headsetcontrol_test.numIdProducts = 1;
device_headsetcontrol_test.equalizer = &EQUALIZER;
device_headsetcontrol_test.eqaulizer_presets = &EQUALIZER_PRESETS;
device_headsetcontrol_test.equalizer_presets = &EQUALIZER_PRESETS;

strncpy(device_headsetcontrol_test.device_name, "HeadsetControl Test device", sizeof(device_headsetcontrol_test.device_name));
// normally filled by hid in main.c
wcsncpy(device_headsetcontrol_test.device_hid_vendorname, L"HeadsetControl", sizeof(device_headsetcontrol_test.device_hid_vendorname) / sizeof(device_headsetcontrol_test.device_hid_vendorname[0]));
wcsncpy(device_headsetcontrol_test.device_hid_productname, L"Test device", sizeof(device_headsetcontrol_test.device_hid_productname) / sizeof(device_headsetcontrol_test.device_hid_productname[0]));

if (test_profile != 10) {
device_headsetcontrol_test.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_NOTIFICATION_SOUND) | B(CAP_LIGHTS) | B(CAP_INACTIVE_TIME) | B(CAP_CHATMIX_STATUS) | B(CAP_VOICE_PROMPTS) | B(CAP_ROTATE_TO_MUTE) | B(CAP_EQUALIZER_PRESET) | B(CAP_EQUALIZER) | B(CAP_MICROPHONE_MUTE_LED_BRIGHTNESS) | B(CAP_MICROPHONE_VOLUME) | B(CAP_VOLUME_LIMITER) | B(CAP_BT_WHEN_POWERED_ON) | B(CAP_BT_CALL_VOLUME);
device_headsetcontrol_test.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_NOTIFICATION_SOUND) | B(CAP_LIGHTS) | B(CAP_INACTIVE_TIME) | B(CAP_CHATMIX_STATUS) | B(CAP_VOICE_PROMPTS) | B(CAP_ROTATE_TO_MUTE) | B(CAP_EQUALIZER_PRESET) | B(CAP_EQUALIZER) | B(CAP_PARAMETRIC_EQUALIZER) | B(CAP_MICROPHONE_MUTE_LED_BRIGHTNESS) | B(CAP_MICROPHONE_VOLUME) | B(CAP_VOLUME_LIMITER) | B(CAP_BT_WHEN_POWERED_ON) | B(CAP_BT_CALL_VOLUME);
} else {
device_headsetcontrol_test.capabilities = B(CAP_SIDETONE) | B(CAP_LIGHTS) | B(CAP_BATTERY_STATUS);
}
Expand All @@ -78,6 +79,7 @@ void headsetcontrol_test_init(struct device** device)
device_headsetcontrol_test.switch_rotate_to_mute = &headsetcontrol_test_switch_rotate_to_mute;
device_headsetcontrol_test.send_equalizer_preset = &headsetcontrol_test_send_equalizer_preset;
device_headsetcontrol_test.send_equalizer = &headsetcontrol_test_send_equalizer;
device_headsetcontrol_test.send_parametric_equalizer = &headsetcontrol_test_send_parametric_equalizer;
device_headsetcontrol_test.send_microphone_mute_led_brightness = &headsetcontrol_test_send_microphone_mute_led_brightness;
device_headsetcontrol_test.send_microphone_volume = &headsetcontrol_test_send_microphone_volume;
device_headsetcontrol_test.send_volume_limiter = &headsetcontrol_test_volume_limiter;
Expand Down Expand Up @@ -147,6 +149,11 @@ static int headsetcontrol_test_send_equalizer(hid_device* device_handle, struct
return TESTBYTES_SEND;
}

static int headsetcontrol_test_send_parametric_equalizer(hid_device* device_handle, struct parametric_equalizer_settings* settings)
{
return TESTBYTES_SEND;
}

static int headsetcontrol_test_send_microphone_mute_led_brightness(hid_device* device_handle, uint8_t num)
{
return TESTBYTES_SEND;
Expand Down
Loading

0 comments on commit e537f2f

Please sign in to comment.