Something went wrong. Try again.
Mixxx DJ software with MCP, Subsonic/Navidrome streaming library support — browse and load tracks from your self-hosted music server mixxx.org
subsonic navidrome streaming audio dj mac linux midi
Something went wrong. Try again.
10 kB · 245 lines
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246#pragma once
#include <QFuture>#include <memory>#include <mutex>#include <optional>
#include "controllers/controller.h"#include "controllers/hid/hiddevice.h"#include "controllers/hid/hidiothread.h"#include "controllers/hid/hidreportdescriptor.h"#include "controllers/hid/legacyhidcontrollermapping.h"
/// HID controller backendclass HidController final : public Controller { Q_OBJECT public: explicit HidController( mixxx::hid::DeviceInfo&& deviceInfo); ~HidController() override;
ControllerJSProxy* jsProxy() override;
QString mappingExtension() override;
void setMapping(std::shared_ptr<LegacyControllerMapping> pMapping) override;
QList<LegacyControllerMapping::ScriptFileInfo> getMappingScriptFiles() override; QList<std::shared_ptr<AbstractLegacyControllerSetting>> getMappingSettings() override;#ifdef MIXXX_USE_QML QList<LegacyControllerMapping::QMLModuleInfo> getMappingModules() override; QList<LegacyControllerMapping::ScreenInfo> getMappingInfoScreens() override;#endif
PhysicalTransportProtocol getPhysicalTransportProtocol() const override { return m_deviceInfo.getPhysicalTransportProtocol(); } DataRepresentationProtocol getDataRepresentationProtocol() const override { return DataRepresentationProtocol::HID; } // Note, that for non-USB devices, the VendorString/ProductString can // contain the driver manufacturer, instead of the actual device // manufacturer. The VID/PID is a more reliable way to identify a HID // device. QString getVendorString() const override { return m_deviceInfo.getVendorString(); } QString getProductString() const override { return m_deviceInfo.getProductString(); } std::optional<uint16_t> getProductId() const override { return m_deviceInfo.getProductId(); } std::optional<uint16_t> getVendorId() const override { return m_deviceInfo.getVendorId(); } QString getSerialNumber() const override { return m_deviceInfo.getSerialNumber(); }
std::optional<uint8_t> getUsbInterfaceNumber() const override { return m_deviceInfo.getUsbInterfaceNumber(); } uint16_t getUsagePage() const { return m_deviceInfo.getUsagePage(); }
uint16_t getUsage() const { return m_deviceInfo.getUsage(); }
QString getUsagePageDescription() const { return m_deviceInfo.getUsagePageDescription(); }
QString getUsageDescription() const { return m_deviceInfo.getUsageDescription(); }
std::shared_ptr<const hid::reportDescriptor::HidReportDescriptor> getReportDescriptor() const { return m_reportDescriptor; }
HidIoThread* getHidIoThread() const { return m_pHidIoThread.get(); }
bool isMappable() const override { if (!m_pMapping) { return false; } return m_pMapping->isMappable(); }
bool matchMapping(const MappingInfo& mapping) override;
// Start fetching the device's HID report descriptor in a background thread. // The fetched raw descriptor is stored inside the DeviceInfo owned by this // HidController instance so it will be available later when the device is // opened for real. void fetchReportDescriptorInBackground();
private: int open(const QString& resourcePath) override; int close() override;
// For devices which only support a single report, reportID must be set to // 0x0. bool sendBytes(const QByteArray& data) override;
mixxx::hid::DeviceInfo m_deviceInfo; // These optional members are not set before opening the device std::shared_ptr<hid::reportDescriptor::HidReportDescriptor> m_reportDescriptor; std::optional<bool> m_deviceUsesReportIds;
std::unique_ptr<HidIoThread> m_pHidIoThread; std::unique_ptr<LegacyHidControllerMapping> m_pMapping;
// Background thread used to fetch raw HID report descriptor during // enumeration so mapping detection can use it without blocking the startup. QFuture<void> m_reportDescriptorFuture;
// Protects access to m_reportDescriptor and m_deviceUsesReportIds from // concurrent access between the background fetch thread // and the hid controller thread that opens the device. mutable std::mutex m_reportDescriptorMutex;
// A persistent lock on m_reportDescriptorMutex, acquired in open() and // released in close(). This prevents the background fetch thread from // modifying the report descriptor while it is being used. When a // unique_lock is reset, it automatically releases the mutex it holds. std::optional<std::unique_lock<std::mutex>> m_reportDescriptorLock;
friend class HidControllerJSProxy;};
class HidControllerJSProxy : public ControllerJSProxy { Q_OBJECT public: HidControllerJSProxy(HidController* m_pController) : ControllerJSProxy(m_pController), m_pHidController(m_pController) { }
/// @brief Sends HID OutputReport with hard coded ReportID 0 to HID device /// This function only works with HID devices, which don't use ReportIDs /// @param dataList Data to send as list of bytes /// @param length Unused optional argument Q_INVOKABLE void send(const QList<int>& dataList, unsigned int length = 0) override { // This function is only for class compatibility with the (midi)controller Q_UNUSED(length); send(dataList, 0, 0); }
/// @brief Sends HID OutputReport to HID device /// @param dataList Data to send as list of bytes /// @param length Unused but mandatory argument /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for /// devices, which don't use ReportIDs /// @param useNonSkippingFIFO (optional) /// Same as argument useNonSkippingFIFO of the sendOutputReport function, /// which is documented below Q_INVOKABLE void send(const QList<int>& dataList, unsigned int length, quint8 reportID, bool useNonSkippingFIFO = false) { Q_UNUSED(length); QByteArray dataArray; dataArray.reserve(dataList.size()); for (int datum : dataList) { dataArray.append(datum); } sendOutputReport(reportID, dataArray, useNonSkippingFIFO); }
/// @brief Sends an OutputReport to HID device /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for devices, which don't use ReportIDs /// @param dataArray Data to send as byte array (Javascript type Uint8Array) /// @param useNonSkippingFIFO (optional) /// - False (default): /// - Reports with identical data will be sent only once. /// - If reports were superseded by newer data before they could be sent, /// the oudated data will be skipped. /// - This mode works for all USB HID class compatible reports, /// in these each field represents the state of a control (e.g. an LED). /// - This mode works best in overload situations, where more reports /// are to be sent, than can be processed. /// - True: /// - The report will not be skipped under any circumstances, /// except FIFO memory overflow. /// - All reports with useNonSkippingFIFO set True will be send before /// any cached report with useNonSkippingFIFO set False. /// - All reports with useNonSkippingFIFO set True will be send in /// strict First In / First Out (FIFO) order. /// - Limit the use of this mode to the places, where it is really necessary. Q_INVOKABLE void sendOutputReport(quint8 reportID, const QByteArray& dataArray, bool useNonSkippingFIFO = false) { VERIFY_OR_DEBUG_ASSERT(m_pHidController->m_pHidIoThread) { return; } m_pHidController->m_pHidIoThread->updateCachedOutputReportData( reportID, dataArray, useNonSkippingFIFO); }
/// @brief getInputReport receives an InputReport from the HID device on request. /// @details This can be used on startup to initialize the knob positions in Mixxx /// to the physical position of the hardware knobs on the controller. /// This is an optional command in the HID standard - not all devices support it. /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for devices, which don't use /// @return Returns report data with ReportID byte as prefix Q_INVOKABLE QByteArray getInputReport( quint8 reportID) { VERIFY_OR_DEBUG_ASSERT(m_pHidController->m_pHidIoThread) { return {}; } return m_pHidController->m_pHidIoThread->getInputReport(reportID); }
/// @brief Sends a FeatureReport to HID device /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for devices, which don't use /// @param reportData Data to send as byte array (Javascript type Uint8Array) Q_INVOKABLE void sendFeatureReport( quint8 reportID, const QByteArray& reportData) { VERIFY_OR_DEBUG_ASSERT(m_pHidController->m_pHidIoThread) { return; } m_pHidController->m_pHidIoThread->sendFeatureReport(reportID, reportData); }
/// @brief getFeatureReport receives a FeatureReport from the HID device on request. /// @param reportID 1...255 for HID devices that uses ReportIDs - or 0 for devices, which don't use /// @return The returned array matches the input format of sendFeatureReport (Javascript type Uint8Array), /// allowing it to be read, modified and sent it back to the controller. Q_INVOKABLE QByteArray getFeatureReport( quint8 reportID) { VERIFY_OR_DEBUG_ASSERT(m_pHidController->m_pHidIoThread) { return {}; } return m_pHidController->m_pHidIoThread->getFeatureReport(reportID); }
private: HidController* m_pHidController;};