diff --git a/lib/devices/audeze_maxwell2.hpp b/lib/devices/audeze_maxwell2.hpp index 2ede0d0..38f56ae 100644 --- a/lib/devices/audeze_maxwell2.hpp +++ b/lib/devices/audeze_maxwell2.hpp @@ -4,6 +4,7 @@ #include "hid_device.hpp" #include #include +#include #include #include @@ -32,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; @@ -220,11 +221,56 @@ 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. + * + * 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. + */ + Result statusFor(hid_device* device_handle) + { + 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; + } + + // 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_ {}; + 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(); } @@ -234,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); @@ -332,7 +380,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(); } @@ -353,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) { @@ -374,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) {