From f77991f2ee7094bdccaf4aa82561544c31d96517 Mon Sep 17 00:00:00 2001 From: AdrianKuriata Date: Wed, 29 Jul 2026 16:04:44 +0200 Subject: [PATCH 1/3] perf(audeze): reuse one status read for all info capabilities getDeviceStatus() returns battery, chatmix, sidetone and eq together, but getBattery() and getChatmix() each called it. On a Maxwell 2 that made -b -m -o json take 2.83 s instead of 1.41 s for one set of values. Cached for 500 ms, keyed on the handle since one instance serves every attached device, and mutex-guarded since it is shared. --- lib/devices/audeze_maxwell2.hpp | 43 +++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/devices/audeze_maxwell2.hpp b/lib/devices/audeze_maxwell2.hpp index 2ede0d0..ebb3a26 100644 --- a/lib/devices/audeze_maxwell2.hpp +++ b/lib/devices/audeze_maxwell2.hpp @@ -4,6 +4,8 @@ #include "hid_device.hpp" #include #include +#include +#include #include #include @@ -220,11 +222,48 @@ class AudezeMaxwell2 : public HIDDevice { return status; } + /** + * @brief Reuse one status read for every info capability. + * + * getDeviceStatus() costs 21 packets at 60 ms and already returns everything, + * but each getter called it again. Keyed on the handle since one instance + * serves every attached Maxwell 2, and locked since that instance is shared. + */ + static constexpr auto STATUS_REUSE_WINDOW = std::chrono::milliseconds(500); + + Result statusFor(hid_device* device_handle) + { + const std::lock_guard guard(status_mutex_); + + if (last_status_ && last_handle_ == device_handle + && std::chrono::steady_clock::now() - read_at_ < STATUS_REUSE_WINDOW) { + return *last_status_; + } + + auto status = getDeviceStatus(device_handle); + if (!status) { + return status.error(); + } + + last_handle_ = device_handle; + last_status_ = *status; + // After the read, not before: it takes ~1.4 s, so a timestamp taken going + // in would already have expired. + read_at_ = std::chrono::steady_clock::now(); + + return *status; + } + + std::mutex status_mutex_; + hid_device* last_handle_ = nullptr; + std::optional last_status_; + std::chrono::steady_clock::time_point read_at_ {}; + public: // Rich Results V2 API Result getBattery(hid_device* device_handle) override { - auto status_result = getDeviceStatus(device_handle); + auto status_result = statusFor(device_handle); if (!status_result) { return status_result.error(); } @@ -332,7 +371,7 @@ class AudezeMaxwell2 : public HIDDevice { Result getChatmix(hid_device* device_handle) override { - auto status_result = getDeviceStatus(device_handle); + auto status_result = statusFor(device_handle); if (!status_result) { return status_result.error(); } From 65c48ddf5664ffced4ecdf9d9ec72a676fa504e0 Mon Sep 17 00:00:00 2001 From: AdrianKuriata Date: Fri, 14 Aug 2026 14:16:53 +0200 Subject: [PATCH 2/3] perf(audeze): drop the lock and invalidate the reused status on write Review feedback: the codebase is single threaded and has no other lock, so the mutex goes. The reused status also carries sidetone, equalizer and noise filter, which setSidetone, setEqualizerPreset and setNoiseFilter all write. Nothing reads those through statusFor today, but a getter added later would see the value from before the write, so the setters now drop the cached status. Moved the doc comment onto statusFor, where it belongs, instead of above the window constant. --- lib/devices/audeze_maxwell2.hpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/devices/audeze_maxwell2.hpp b/lib/devices/audeze_maxwell2.hpp index ebb3a26..0a30fa8 100644 --- a/lib/devices/audeze_maxwell2.hpp +++ b/lib/devices/audeze_maxwell2.hpp @@ -4,7 +4,6 @@ #include "hid_device.hpp" #include #include -#include #include #include #include @@ -222,19 +221,22 @@ class AudezeMaxwell2 : public HIDDevice { return status; } + static constexpr auto STATUS_REUSE_WINDOW = std::chrono::milliseconds(500); + /** * @brief Reuse one status read for every info capability. * * getDeviceStatus() costs 21 packets at 60 ms and already returns everything, * but each getter called it again. Keyed on the handle since one instance - * serves every attached Maxwell 2, and locked since that instance is shared. + * serves every attached Maxwell 2. + * + * The reused status also carries sidetone, equalizer and noise filter, so + * every setter that writes one of those calls invalidateStatus(). A getter + * built on top of this must keep that list in sync, otherwise a set followed + * by a get inside the window returns the value from before the write. */ - static constexpr auto STATUS_REUSE_WINDOW = std::chrono::milliseconds(500); - Result statusFor(hid_device* device_handle) { - const std::lock_guard guard(status_mutex_); - if (last_status_ && last_handle_ == device_handle && std::chrono::steady_clock::now() - read_at_ < STATUS_REUSE_WINDOW) { return *last_status_; @@ -254,7 +256,12 @@ class AudezeMaxwell2 : public HIDDevice { return *status; } - std::mutex status_mutex_; + // Drop the reused status after a write that changes one of its fields. + void invalidateStatus() + { + last_status_.reset(); + } + hid_device* last_handle_ = nullptr; std::optional last_status_; std::chrono::steady_clock::time_point read_at_ {}; @@ -273,6 +280,8 @@ class AudezeMaxwell2 : public HIDDevice { Result setSidetone(hid_device* device_handle, uint8_t level) override { + invalidateStatus(); + // Maxwell range: 0 to 31 uint8_t mapped = map(level, 0, 128, 0, 31); @@ -392,6 +401,8 @@ class AudezeMaxwell2 : public HIDDevice { Result setEqualizerPreset(hid_device* device_handle, uint8_t preset) override { + invalidateStatus(); + // Maxwell supports presets 0-9 (mapped to device presets 1-10) // 0-5 are built-in presets, 6-9 are custom presets if (preset >= EQUALIZER_PRESETS_COUNT) { @@ -413,6 +424,8 @@ class AudezeMaxwell2 : public HIDDevice { Result setNoiseFilter(hid_device* device_handle, uint8_t level) override { + invalidateStatus(); + // Maxwell 2 has three levels for the noise filter: high, low, and // off (2,1 and 0) if (level > 2) { From 6c009d08792f5f35b4c48e7a44cc97f9de3473b1 Mon Sep 17 00:00:00 2001 From: AdrianKuriata Date: Fri, 14 Aug 2026 14:25:33 +0200 Subject: [PATCH 3/3] style: satisfy clang-format 18 in audeze_maxwell2.hpp cpp-linter checks whole files, not changed lines, and the trailing comment on the Xbox product id predates this PR. It is not reachable from master's own CI, which only runs clang-format on pull requests, so it went unnoticed. clang-format 18.1.3 output, nothing hand-written. --- lib/devices/audeze_maxwell2.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/devices/audeze_maxwell2.hpp b/lib/devices/audeze_maxwell2.hpp index 0a30fa8..38f56ae 100644 --- a/lib/devices/audeze_maxwell2.hpp +++ b/lib/devices/audeze_maxwell2.hpp @@ -33,7 +33,7 @@ class AudezeMaxwell2 : public HIDDevice { public: static constexpr std::array SUPPORTED_PRODUCT_IDS { 0x4b29, // Maxwell 2 (PlayStation/PC version) - 0x4b28 // Maxwell 2 (Xbox version) + 0x4b28 // Maxwell 2 (Xbox version) }; static constexpr int MSG_SIZE = 62;