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
75 changes: 74 additions & 1 deletion cli/output/output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,25 @@ void processFeatureRequest(const FeatureRequest& req, DeviceData& dev, std::stri
}
}

// Equalizer presets count
// Equalizer presets count and presets
if (device_caps & B(CAP_EQUALIZER_PRESET)) {
auto presets_count = hid_device->getEqualizerPresetsCount();
if (presets_count > 0) {
dev.equalizer_presets_count = presets_count;
}

// Get actual preset data if available
auto presets = hid_device->getEqualizerPresets();
if (presets.has_value() && !presets->empty()) {
std::vector<EqualizerPresetData> preset_data;
for (const auto& preset : presets->presets) {
preset_data.push_back(EqualizerPresetData {
.name = preset.name,
.values = preset.values
});
}
dev.equalizer_presets = std::move(preset_data);
}
}

// Process feature requests
Expand Down Expand Up @@ -250,6 +263,32 @@ void outputYaml(const OutputData& data)
dev.battery->serialize(s);
}

if (dev.equalizer.has_value()) {
dev.equalizer->serialize(s);
}

if (dev.equalizer_presets_count.has_value()) {
s.write("equalizer_presets_count", *dev.equalizer_presets_count);
}

if (dev.equalizer_presets.has_value() && !dev.equalizer_presets->empty()) {
s.beginObject("equalizer_presets");
for (const auto& preset : *dev.equalizer_presets) {
// Convert name to lowercase for API compatibility
std::string lower_name;
lower_name.reserve(preset.name.size());
for (char c : preset.name) {
lower_name += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
}
s.writeArray(lower_name, preset.values);
}
s.endObject();
}

if (dev.parametric_eq.has_value()) {
dev.parametric_eq->serialize(s);
}

if (dev.chatmix.has_value()) {
s.write("chatmix", *dev.chatmix);
}
Expand Down Expand Up @@ -327,6 +366,40 @@ void outputEnv(const OutputData& data)
s.writeOptional(prefix + "_BATTERY_TIME_TO_EMPTY_MIN", dev.battery->time_to_empty_min);
}

if (dev.equalizer.has_value()) {
s.write(prefix + "_EQUALIZER_BANDS", dev.equalizer->bands_count);
s.write(prefix + "_EQUALIZER_BASELINE", dev.equalizer->baseline);
s.write(prefix + "_EQUALIZER_STEP", static_cast<double>(dev.equalizer->step));
s.write(prefix + "_EQUALIZER_MIN", dev.equalizer->min);
s.write(prefix + "_EQUALIZER_MAX", dev.equalizer->max);
}

if (dev.equalizer_presets_count.has_value()) {
s.write(prefix + "_EQUALIZER_PRESETS_COUNT", *dev.equalizer_presets_count);
}

if (dev.equalizer_presets.has_value() && !dev.equalizer_presets->empty()) {
for (const auto& preset : *dev.equalizer_presets) {
// Convert name to uppercase for ENV variable naming
std::string upper_name;
upper_name.reserve(preset.name.size());
for (char c : preset.name) {
if (c == ' ')
upper_name += '_';
else
upper_name += static_cast<char>(std::toupper(static_cast<unsigned char>(c)));
}
// Output values as comma-separated string
std::string values_str;
for (size_t k = 0; k < preset.values.size(); ++k) {
if (k > 0)
values_str += ",";
values_str += std::format("{:.1f}", preset.values[k]);
}
s.write(std::format("{}_EQUALIZER_PRESET_{}", prefix, upper_name), values_str);
}
}

if (dev.chatmix.has_value()) {
s.write(prefix + "_CHATMIX", *dev.chatmix);
}
Expand Down
20 changes: 20 additions & 0 deletions cli/output/output_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ struct ErrorData {
std::string message;
};

struct EqualizerPresetData {
std::string name;
std::vector<float> values;
};

struct EqualizerData {
int bands_count = 0;
int baseline = 0;
Expand Down Expand Up @@ -175,6 +180,7 @@ struct DeviceData {
std::optional<int> chatmix;
std::optional<EqualizerData> equalizer;
std::optional<int> equalizer_presets_count;
std::optional<std::vector<EqualizerPresetData>> equalizer_presets;
std::optional<ParametricEqData> parametric_eq;

std::vector<ActionData> actions;
Expand Down Expand Up @@ -205,6 +211,20 @@ struct DeviceData {
s.write("equalizer_presets_count", *equalizer_presets_count);
}

if (equalizer_presets.has_value() && !equalizer_presets->empty()) {
s.beginObject("equalizer_presets");
for (const auto& preset : *equalizer_presets) {
// Convert name to lowercase for API compatibility
std::string lower_name;
lower_name.reserve(preset.name.size());
for (char c : preset.name) {
lower_name += static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
}
s.writeArray(lower_name, preset.values);
}
s.endObject();
}

if (parametric_eq.has_value()) {
parametric_eq->serialize(s);
}
Expand Down
12 changes: 12 additions & 0 deletions lib/devices/headsetcontrol_test.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,18 @@ class HeadsetControlTest : public HIDDevice {
return 4;
}

std::optional<EqualizerPresets> getEqualizerPresets() const override
{
EqualizerPresets presets;
presets.presets = {
{ "Flat", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ "Bass Boost", { 6.0f, 4.0f, 2.0f, 0, 0, 0, 0, 0, 0, 0 } },
{ "Treble Boost", { 0, 0, 0, 0, 0, 0, 2.0f, 4.0f, 6.0f, 6.0f } },
{ "V-Shape", { 4.0f, 2.0f, 0, -2.0f, -2.0f, -2.0f, 0, 2.0f, 4.0f, 4.0f } }
};
return presets;
}

// Rich Results V2 API
Result<BatteryResult> getBattery([[maybe_unused]] hid_device* device_handle) override
{
Expand Down
13 changes: 13 additions & 0 deletions lib/devices/steelseries_arctis_7_plus.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ class SteelSeriesArctis7Plus : public protocols::SteelSeriesNovaDevice<SteelSeri
return EQUALIZER_PRESETS_COUNT;
}

std::optional<EqualizerPresets> getEqualizerPresets() const override
{
// Convert raw byte values to dB using: value = (raw - EQUALIZER_BASELINE) / 2.0f
EqualizerPresets presets;
presets.presets = {
{ "Flat", { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ "Bass Boost", { 3.5f, 4.0f, 1.0f, -1.5f, -1.5f, -1.0f, -1.0f, -1.0f, -1.0f, 5.5f } },
{ "Smiley", { 3.0f, 1.5f, -1.5f, -4.0f, -4.0f, -2.5f, 1.5f, 3.0f, 4.0f, 3.5f } },
{ "Focus", { -5.0f, -1.0f, -3.5f, -2.5f, 4.0f, 6.0f, 3.5f, -3.5f, 0.0f, -3.5f } }
};
return presets;
}

// Rich Results V2 API
Result<BatteryResult> getBattery(hid_device* device_handle) override
{
Expand Down
13 changes: 13 additions & 0 deletions lib/devices/steelseries_arctis_nova_3.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ class SteelSeriesArctisNova3 : public protocols::SteelSeriesNovaDevice<SteelSeri
return EQUALIZER_PRESETS_COUNT;
}

std::optional<EqualizerPresets> getEqualizerPresets() const override
{
// Convert raw byte values to dB using: value = (raw - EQUALIZER_BASELINE) / 2.0f
EqualizerPresets presets;
presets.presets = {
{ "Flat", { 0, 0, 0, 0, 0, 0 } },
{ "Bass", { 4.0f, 2.5f, -1.5f, 0, 0, 0 } },
{ "Smiley", { 3.0f, 1.5f, -2.5f, -1.0f, 1.5f, 3.0f } },
{ "Focus", { -4.0f, -3.5f, -1.5f, 2.0f, 4.0f, 0 } }
};
return presets;
}

// Rich Results V2 API
Result<SidetoneResult> setSidetone(hid_device* device_handle, uint8_t level) override
{
Expand Down
12 changes: 12 additions & 0 deletions lib/devices/steelseries_arctis_nova_3p_wireless.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ class SteelSeriesArctisNova3PWireless : public protocols::SteelSeriesNovaDevice<
return EQUALIZER_PRESETS_COUNT;
}

std::optional<EqualizerPresets> getEqualizerPresets() const override
{
EqualizerPresets presets;
presets.presets = {
{ "Flat", std::vector<float>(PRESET_FLAT.begin(), PRESET_FLAT.end()) },
{ "Bass", std::vector<float>(PRESET_BASS.begin(), PRESET_BASS.end()) },
{ "Focus", std::vector<float>(PRESET_FOCUS.begin(), PRESET_FOCUS.end()) },
{ "Smiley", std::vector<float>(PRESET_SMILEY.begin(), PRESET_SMILEY.end()) }
};
return presets;
}

std::optional<ParametricEqualizerInfo> getParametricEqualizerInfo() const override
{
// Supported filters: LowShelf, LowPass, Peaking, HighPass, HighShelf, Notch
Expand Down
12 changes: 12 additions & 0 deletions lib/devices/steelseries_arctis_nova_5.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ class SteelSeriesArctisNova5 : public protocols::SteelSeriesNovaDevice<SteelSeri
return EQUALIZER_PRESETS_COUNT;
}

std::optional<EqualizerPresets> getEqualizerPresets() const override
{
EqualizerPresets presets;
presets.presets = {
{ "Flat", std::vector<float>(PRESET_FLAT.begin(), PRESET_FLAT.end()) },
{ "Bass", std::vector<float>(PRESET_BASS.begin(), PRESET_BASS.end()) },
{ "Focus", std::vector<float>(PRESET_FOCUS.begin(), PRESET_FOCUS.end()) },
{ "Smiley", std::vector<float>(PRESET_SMILEY.begin(), PRESET_SMILEY.end()) }
};
return presets;
}

std::optional<ParametricEqualizerInfo> getParametricEqualizerInfo() const override
{
// Supported filters: LowShelf, LowPass, Peaking, HighPass, HighShelf
Expand Down
12 changes: 12 additions & 0 deletions lib/devices/steelseries_arctis_nova_7.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ class SteelSeriesArctisNova7 : public protocols::SteelSeriesNovaDevice<SteelSeri
return EQUALIZER_PRESETS_COUNT;
}

std::optional<EqualizerPresets> getEqualizerPresets() const override
{
EqualizerPresets presets;
presets.presets = {
{ "Flat", std::vector<float>(PRESET_FLAT.begin(), PRESET_FLAT.end()) },
{ "Bass", std::vector<float>(PRESET_BASS.begin(), PRESET_BASS.end()) },
{ "Focus", std::vector<float>(PRESET_FOCUS.begin(), PRESET_FOCUS.end()) },
{ "Smiley", std::vector<float>(PRESET_SMILEY.begin(), PRESET_SMILEY.end()) }
};
return presets;
}

constexpr capability_detail getCapabilityDetail([[maybe_unused]] enum capabilities cap) const override
{
return { .usagepage = 0xffc0, .usageid = 0x1, .interface_id = 3 };
Expand Down