Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ talking. This differs from a simple loopback via PulseAudio as you won't have an
- SteelSeries Arctis (7 and Pro)
- Sidetone, Battery, Inactive time, Chat-Mix level, LED on/off (allows to turn off the blinking LED on the base-station)
- SteelSeries Arctis 7+
- Sidetone, Battery, Inactive time, Equalizer Presets
- Sidetone, Battery, Chat-Mix level, Inactive time, Equalizer Presets, Equalizer
- SteelSeries Arctis Nova 7
- Sidetone, Battery, Chat-Mix level, Inactive time, Equalizer Presets
- Sidetone, Battery, Chat-Mix level, Inactive time, Equalizer Presets, Equalizer
- SteelSeries Arctis 9
- Sidetone, Battery, Inactive time, Chat-Mix level
- SteelSeries Arctis Pro Wireless
Expand Down Expand Up @@ -167,6 +167,8 @@ The following options don't work on all devices yet:

`headsetcontrol -p 0-3` sets equalizer preset, must be between 0 and 3, 0 is the default preset.

`headsetcontrol -e string` sets equalizer to specified curve, string must contain band values specific to the device (hex or decimal) delimited by spaces, or commas, or new-lines e.g "0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18".

##### Modifiers

`--timeout 5000` Specifies a timeout for read-operations in milliseconds. Default is 5 seconds, 0 disables timeout.
Expand Down
38 changes: 2 additions & 36 deletions src/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "hid_utility.h"

#include "utility.h"

#include <hidapi.h>

#include <getopt.h>
Expand Down Expand Up @@ -39,42 +41,6 @@ static void print_devices(unsigned short vendorid, unsigned short productid)
hid_free_enumeration(devs);
}

/**
* @brief Accepts textual input and converts them to a sendable buffer
*
* Parses data like "0xff, 123, 0xb" and converts them to an array of len 3
*
* @param input string
* @param dest destination array
* @param len max dest length
* @return int amount of data converted
*/
static int get_data_from_parameter(char* input, char* dest, size_t len)
{
const char* delim = " ,{}\n\r";

size_t sz = strlen(input);
char* str = (char*)malloc(sz + 1);
strcpy(str, input);

// For each token in the string, parse and store in buf[].
char* token = strtok(input, delim);
int i = 0;
while (token) {
char* endptr;
long int val = strtol(token, &endptr, 0);

if (i >= len)
return -1;

dest[i++] = val;
token = strtok(NULL, delim);
}

free(str);
return i;
}

/**
* @brief check if number inside range
*
Expand Down
2 changes: 2 additions & 0 deletions src/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const char* const capabilities_str[NUM_CAPABILITIES]
[CAP_VOICE_PROMPTS] = "voice prompts",
[CAP_ROTATE_TO_MUTE] = "rotate to mute",
[CAP_EQUALIZER_PRESET] = "equalizer preset",
[CAP_EQUALIZER] = "equalizer",
};

const char capabilities_str_short[NUM_CAPABILITIES]
Expand All @@ -24,4 +25,5 @@ const char capabilities_str_short[NUM_CAPABILITIES]
[CAP_VOICE_PROMPTS] = 'v',
[CAP_ROTATE_TO_MUTE] = 'r',
[CAP_EQUALIZER_PRESET] = 'p',
[CAP_EQUALIZER] = 'e'
};
26 changes: 26 additions & 0 deletions src/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum capabilities {
CAP_VOICE_PROMPTS,
CAP_ROTATE_TO_MUTE,
CAP_EQUALIZER_PRESET,
CAP_EQUALIZER,
NUM_CAPABILITIES
};

Expand Down Expand Up @@ -59,6 +60,15 @@ enum headsetcontrol_errors {
HSC_OUT_OF_BOUNDS = -102,
};

/** @brief Defines equalizer custom setings
*/
struct equalizer_settings {
/// The size of the bands array
int size;
/// The equalizer frequency bands values
char bands_values[];
};

/** @brief Defines the basic data of a device
*
* Also defines function pointers for using supported features
Expand Down Expand Up @@ -204,4 +214,20 @@ struct device {
* -1 HIDAPI error
*/
int (*send_equalizer_preset)(hid_device* hid_device, uint8_t num);

/** @brief Function pointer for setting headset 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 equalizer settings containing
* the frequency bands values
*
* @returns > 0 on success
* HSC_OUT_OF_BOUNDS on equalizer settings size out of range
* specific to this hardware
* -1 HIDAPI error
*/
int (*send_equalizer)(hid_device* hid_device, struct equalizer_settings* settings);
};
24 changes: 23 additions & 1 deletion src/devices/steelseries_arctis_7_plus.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ static struct device device_arctis;
#define HEADSET_OFFLINE 0x01
#define STATUS_BUF_SIZE 6

#define EQUALIZER_BANDS_SIZE 10

static const uint16_t PRODUCT_IDS[] = { ID_ARCTIS_7_PLUS, ID_ARCTIS_7_PLUS_PS5 };

static int arctis_7_plus_send_sidetone(hid_device* device_handle, uint8_t num);
static int arctis_7_plus_send_inactive_time(hid_device* device_handle, uint8_t num);
static int arctis_7_plus_send_equalizer_preset(hid_device* device_handle, uint8_t num);
static int arctis_7_plus_send_equalizer(hid_device* device_handle, struct equalizer_settings* settings);
static int arctis_7_plus_request_battery(hid_device* device_handle);
static int arctis_7_plus_request_chatmix(hid_device* device_handle);

Expand All @@ -37,18 +40,20 @@ void arctis_7_plus_init(struct device** device)

strncpy(device_arctis.device_name, "SteelSeries Arctis 7+", sizeof(device_arctis.device_name));

device_arctis.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_CHATMIX_STATUS) | B(CAP_INACTIVE_TIME) | B(CAP_EQUALIZER_PRESET);
device_arctis.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_CHATMIX_STATUS) | B(CAP_INACTIVE_TIME) | B(CAP_EQUALIZER) | B(CAP_EQUALIZER_PRESET);
device_arctis.capability_details[CAP_SIDETONE] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_BATTERY_STATUS] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_CHATMIX_STATUS] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_INACTIVE_TIME] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_EQUALIZER_PRESET] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_EQUALIZER] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };

device_arctis.send_sidetone = &arctis_7_plus_send_sidetone;
device_arctis.request_battery = &arctis_7_plus_request_battery;
device_arctis.request_chatmix = &arctis_7_plus_request_chatmix;
device_arctis.send_inactive_time = &arctis_7_plus_send_inactive_time;
device_arctis.send_equalizer_preset = &arctis_7_plus_send_equalizer_preset;
device_arctis.send_equalizer = &arctis_7_plus_send_equalizer;

*device = &device_arctis;
}
Expand Down Expand Up @@ -147,6 +152,7 @@ static int arctis_7_plus_send_equalizer_preset(hid_device* device_handle, uint8_

switch (num) {
case 0: {

uint8_t flat[MSG_SIZE] = { 0x0, 0x33, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0 };
return hid_write(device_handle, flat, MSG_SIZE);
}
Expand All @@ -169,6 +175,22 @@ static int arctis_7_plus_send_equalizer_preset(hid_device* device_handle, uint8_
}
}

static int arctis_7_plus_send_equalizer(hid_device* device_handle, struct equalizer_settings* settings)
{
if (settings->size != EQUALIZER_BANDS_SIZE) {
printf("Device only supports %d bands.\n", EQUALIZER_BANDS_SIZE);
return HSC_OUT_OF_BOUNDS;
}

uint8_t data[MSG_SIZE] = { 0x0, 0x33 };
for (int i = 0; i < settings->size; i++) {
data[i + 2] = (uint8_t)settings->bands_values[i];
}
data[settings->size + 3] = 0x0;

return hid_write(device_handle, data, MSG_SIZE);
}

int arctis_7_plus_read_device_status(hid_device* device_handle, unsigned char* data_read)
{
unsigned char data_request[2] = { 0x00, 0xb0 };
Expand Down
23 changes: 22 additions & 1 deletion src/devices/steelseries_arctis_nova_7.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ static struct device device_arctis;
#define HEADSET_OFFLINE 0x00
#define STATUS_BUF_SIZE 8

#define EQUALIZER_BANDS_SIZE 10

static const uint16_t PRODUCT_IDS[] = { ID_ARCTIS_NOVA_7, ID_ARCTIS_NOVA_7x, ID_ARCTIS_NOVA_7p };

static int arctis_nova_7_send_sidetone(hid_device* device_handle, uint8_t num);
static int arctis_nova_7_send_inactive_time(hid_device* device_handle, uint8_t num);
static int arctis_nova_7_send_equalizer_preset(hid_device* device_handle, uint8_t num);
static int arctis_nova_7_send_equalizer(hid_device* device_handle, struct equalizer_settings* settings);
static int arctis_nova_7_request_battery(hid_device* device_handle);
static int arctis_nova_7_request_chatmix(hid_device* device_handle);

Expand All @@ -38,18 +41,20 @@ void arctis_nova_7_init(struct device** device)

strncpy(device_arctis.device_name, "SteelSeries Arctis Nova 7", sizeof(device_arctis.device_name));

device_arctis.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_CHATMIX_STATUS) | B(CAP_INACTIVE_TIME) | B(CAP_EQUALIZER_PRESET);
device_arctis.capabilities = B(CAP_SIDETONE) | B(CAP_BATTERY_STATUS) | B(CAP_CHATMIX_STATUS) | B(CAP_INACTIVE_TIME) | B(CAP_EQUALIZER) | B(CAP_EQUALIZER_PRESET);
device_arctis.capability_details[CAP_SIDETONE] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_BATTERY_STATUS] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_CHATMIX_STATUS] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_INACTIVE_TIME] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_EQUALIZER_PRESET] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };
device_arctis.capability_details[CAP_EQUALIZER] = (struct capability_detail) { .usagepage = 0xffc0, .usageid = 0x1, .interface = 3 };

device_arctis.send_sidetone = &arctis_nova_7_send_sidetone;
device_arctis.request_battery = &arctis_nova_7_request_battery;
device_arctis.request_chatmix = &arctis_nova_7_request_chatmix;
device_arctis.send_inactive_time = &arctis_nova_7_send_inactive_time;
device_arctis.send_equalizer_preset = &arctis_nova_7_send_equalizer_preset;
device_arctis.send_equalizer = &arctis_nova_7_send_equalizer;

*device = &device_arctis;
}
Expand Down Expand Up @@ -173,6 +178,22 @@ static int arctis_nova_7_send_equalizer_preset(hid_device* device_handle, uint8_
}
}

static int arctis_nova_7_send_equalizer(hid_device* device_handle, struct equalizer_settings* settings)
{
if (settings->size != EQUALIZER_BANDS_SIZE) {
printf("Device only supports %d bands.\n", EQUALIZER_BANDS_SIZE);
return HSC_OUT_OF_BOUNDS;
}

uint8_t data[MSG_SIZE] = { 0x0, 0x33 };
for (int i = 0; i < settings->size; i++) {
data[i + 2] = (uint8_t)settings->bands_values[i];
}
data[settings->size + 3] = 0x0;

return hid_write(device_handle, data, MSG_SIZE);
}

int arctis_nova_7_read_device_status(hid_device* device_handle, unsigned char* data_read)
{
unsigned char data_request[2] = { 0x00, 0xb0 };
Expand Down
Loading