From 2a0568cafcf1494b358058b41b78bcf7ba3497a2 Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 31 Aug 2023 23:27:08 +0200 Subject: [PATCH 1/9] Effect chain preset selector: show preset effects in tooltip --- src/widget/weffectchainpresetselector.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/widget/weffectchainpresetselector.cpp b/src/widget/weffectchainpresetselector.cpp index f8a0a924e54..f854d6fa9b9 100644 --- a/src/widget/weffectchainpresetselector.cpp +++ b/src/widget/weffectchainpresetselector.cpp @@ -74,13 +74,21 @@ void WEffectChainPresetSelector::populate() { presetList = m_pEffectsManager->getChainPresetManager()->getPresetsSorted(); } + const EffectsBackendManagerPointer bem = m_pEffectsManager->getBackendManager(); for (int i = 0; i < presetList.size(); i++) { auto pChainPreset = presetList.at(i); QString elidedDisplayName = metrics.elidedText(pChainPreset->name(), Qt::ElideMiddle, view()->width() - 2); addItem(elidedDisplayName, QVariant(pChainPreset->name())); - setItemData(i, pChainPreset->name(), Qt::ToolTipRole); + QStringList tooltip; + tooltip.append(QStringLiteral("") + pChainPreset->name() + QStringLiteral("")); + for (const auto& pEffectPreset : pChainPreset->effectPresets()) { + if (!pEffectPreset->isEmpty()) { + tooltip.append(bem->getDisplayNameForEffectPreset(pEffectPreset)); + } + } + setItemData(i, tooltip.join("
"), Qt::ToolTipRole); } slotChainPresetChanged(m_pChain->presetName()); From 3405e10fb0460c773034b2396f7ee0bc37db5eea Mon Sep 17 00:00:00 2001 From: ronso0 Date: Thu, 31 Aug 2023 23:54:49 +0200 Subject: [PATCH 2/9] Effect chain preset menu: show preset effects in tooltip --- src/widget/weffectchainpresetbutton.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/widget/weffectchainpresetbutton.cpp b/src/widget/weffectchainpresetbutton.cpp index e4cb5d3128b..b0735862f70 100644 --- a/src/widget/weffectchainpresetbutton.cpp +++ b/src/widget/weffectchainpresetbutton.cpp @@ -5,6 +5,7 @@ #include "effects/presets/effectpresetmanager.h" #include "moc_weffectchainpresetbutton.cpp" +#include "util/parented_ptr.h" #include "widget/effectwidgetutils.h" WEffectChainPresetButton::WEffectChainPresetButton(QWidget* parent, EffectsManager* pEffectsManager) @@ -43,6 +44,7 @@ void WEffectChainPresetButton::setup(const QDomNode& node, const SkinContext& co this, &WEffectChainPresetButton::populateMenu); } + m_pMenu->setToolTipsVisible(true); populateMenu(); } @@ -50,6 +52,7 @@ void WEffectChainPresetButton::populateMenu() { m_pMenu->clear(); // Chain preset items + const EffectsBackendManagerPointer bem = m_pEffectsManager->getBackendManager(); bool presetIsReadOnly = true; for (const auto& pChainPreset : m_pChainPresetManager->getPresetsSorted()) { QString title = pChainPreset->name(); @@ -58,9 +61,22 @@ void WEffectChainPresetButton::populateMenu() { QChar(' ') + title; presetIsReadOnly = pChainPreset->isReadOnly(); } - m_pMenu->addAction(title, this, [this, pChainPreset]() { - m_pChain->loadChainPreset(pChainPreset); - }); + QStringList tooltip; + tooltip.append(QStringLiteral("") + pChainPreset->name() + QStringLiteral("")); + for (const auto& pEffectPreset : pChainPreset->effectPresets()) { + if (!pEffectPreset->isEmpty()) { + tooltip.append(bem->getDisplayNameForEffectPreset(pEffectPreset)); + } + } + parented_ptr pAction = make_parented(title, this); + connect(pAction, + &QAction::triggered, + this, + [this, pChainPreset]() { + m_pChain->loadChainPreset(pChainPreset); + }); + pAction->setToolTip(tooltip.join("
")); + m_pMenu->addAction(pAction); } m_pMenu->addSeparator(); // This prevents showing the Update button for the empty '---' preset, in case From ea084ce1945fcbfcf30c94d42392428cb79f7c3b Mon Sep 17 00:00:00 2001 From: ronso0 Date: Tue, 5 Sep 2023 00:50:10 +0200 Subject: [PATCH 3/9] Effect chain preset: show contained effects in tooltip only if there's more than one --- src/widget/weffectchainpresetbutton.cpp | 13 +++++++++---- src/widget/weffectchainpresetselector.cpp | 13 +++++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/widget/weffectchainpresetbutton.cpp b/src/widget/weffectchainpresetbutton.cpp index b0735862f70..e33e46809ff 100644 --- a/src/widget/weffectchainpresetbutton.cpp +++ b/src/widget/weffectchainpresetbutton.cpp @@ -61,13 +61,18 @@ void WEffectChainPresetButton::populateMenu() { QChar(' ') + title; presetIsReadOnly = pChainPreset->isReadOnly(); } - QStringList tooltip; - tooltip.append(QStringLiteral("") + pChainPreset->name() + QStringLiteral("")); + QString tooltip = + QStringLiteral("") + pChainPreset->name() + QStringLiteral(""); + QStringList effectNames; for (const auto& pEffectPreset : pChainPreset->effectPresets()) { if (!pEffectPreset->isEmpty()) { - tooltip.append(bem->getDisplayNameForEffectPreset(pEffectPreset)); + effectNames.append(bem->getDisplayNameForEffectPreset(pEffectPreset)); } } + if (effectNames.size() > 1) { + tooltip.append("
"); + tooltip.append(effectNames.join("
")); + } parented_ptr pAction = make_parented(title, this); connect(pAction, &QAction::triggered, @@ -75,7 +80,7 @@ void WEffectChainPresetButton::populateMenu() { [this, pChainPreset]() { m_pChain->loadChainPreset(pChainPreset); }); - pAction->setToolTip(tooltip.join("
")); + pAction->setToolTip(tooltip); m_pMenu->addAction(pAction); } m_pMenu->addSeparator(); diff --git a/src/widget/weffectchainpresetselector.cpp b/src/widget/weffectchainpresetselector.cpp index f854d6fa9b9..d106b8e602e 100644 --- a/src/widget/weffectchainpresetselector.cpp +++ b/src/widget/weffectchainpresetselector.cpp @@ -81,14 +81,19 @@ void WEffectChainPresetSelector::populate() { Qt::ElideMiddle, view()->width() - 2); addItem(elidedDisplayName, QVariant(pChainPreset->name())); - QStringList tooltip; - tooltip.append(QStringLiteral("") + pChainPreset->name() + QStringLiteral("")); + QString tooltip = + QStringLiteral("") + pChainPreset->name() + QStringLiteral(""); + QStringList effectNames; for (const auto& pEffectPreset : pChainPreset->effectPresets()) { if (!pEffectPreset->isEmpty()) { - tooltip.append(bem->getDisplayNameForEffectPreset(pEffectPreset)); + effectNames.append(bem->getDisplayNameForEffectPreset(pEffectPreset)); } } - setItemData(i, tooltip.join("
"), Qt::ToolTipRole); + if (effectNames.size() > 1) { + tooltip.append("
"); + tooltip.append(effectNames.join("
")); + } + setItemData(i, tooltip, Qt::ToolTipRole); } slotChainPresetChanged(m_pChain->presetName()); From 8e07fe816c3550f1b6810d70c8003178d57e9cbf Mon Sep 17 00:00:00 2001 From: Joerg Date: Wed, 6 Sep 2023 01:10:22 +0200 Subject: [PATCH 4/9] -TypedArray is no public JS keyword, we need to list them all, not only for createDataView, but also for it's callers -Last 3 arguments of addControl are optional (but was not defined this way in JSDoc) -length argument of parsePacket is only needed for a warning message -> make it optional --- res/controllers/common-hid-packet-parser.js | 166 +++++--------------- 1 file changed, 35 insertions(+), 131 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 32663390f09..ea2016c5eff 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -2,7 +2,6 @@ /** * Common HID script debugging function. Just to get logging with 'HID' prefix. - * * @deprecated Use console.log instead * @param {any} message Message to be printed on controller debug console output */ @@ -12,8 +11,7 @@ this.HIDDebug = function(message) { /** * creates a `DataView` from any ArrayBuffer, TypedArray * or plain Array (clamped to 8-bit width). - * - * @param {number[] | ArrayBuffer | TypedArray} bufferLike Object that can be represented as a sequence of bytes + * @param {number[] | ArrayBuffer | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array } bufferLike Object that can be represented as a sequence of bytes * @returns {DataView} dataview over the bufferLike object */ const createDataView = function(bufferLike) { @@ -33,20 +31,17 @@ const createDataView = function(bufferLike) { * InputReport are received. If a packet callback is defined and the data for the InputReport are * received, the complete report data are sent to the callback function after field values are * parsed, without calling any packet field parsing functions. - * * @callback packetCallback * @param {HIDPacket} packet The packet that represents the InputReport * @param {Object.} changed_data The data received from the device */ /** * Callback function to call when, the value of a modifier control changed - * * @callback modifierCallback * @param {boolean} Value of the modifier control */ /** * Callback function to call when, data for specified filed in the packet is updated. - * * @callback fieldChangeCallback * @param {packetField|bitObject} Object that describes a field/bit inside of a packet, which can often * mapped to a Mixxx control. @@ -54,7 +49,6 @@ const createDataView = function(bufferLike) { /** * Callback function, which will be called every time, the value of the connected control changes. - * * @callback controlCallback * @param {number} value New value of the control * @param {string} group Mixxx control group name e.g. "[Channel1]" @@ -71,7 +65,6 @@ const createDataView = function(bufferLike) { * The ScallingCallback function can also have a boolean property .useSetParameter, if: * - 'false' or 'undefined', engine.setValue is used * - 'true' engine.setParameter is used - * * @callback scalingCallback * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name "pregain" @@ -82,7 +75,6 @@ const createDataView = function(bufferLike) { /** * Callback function to call when, jog wheel scratching got enabled or disabled by * the button with the special name 'jog_touch' - * * @callback scratchingCallback * @param {boolean} isScratchEnabled True, when button 'jog_touch' is active */ @@ -153,20 +145,17 @@ class HIDBitVector { constructor() { /** * Number of bitObjects in bits array - * * @type {number} */ this.size = 0; /** * Object of bitObjects, referred by a string of group and control name separated by a dot - * * @type {Object.} */ this.bits = {}; } /** * Get the index of the least significant bit that is 1 in `bitmask` - * * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are * considered. * @returns {number} Index of the least significant bit that is 1 in `bitmask` @@ -183,7 +172,6 @@ class HIDBitVector { } /** * Add a control bitmask to the HIDBitVector - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name e.g. "play" * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are @@ -209,7 +197,6 @@ class HIDBitVector { } /** * Add an output control bitmask to the HIDBitVector - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name e.g. "play" * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are @@ -251,21 +238,18 @@ class HIDModifierList { constructor() { /** * Actual value of the modifier - * * @type {Object.} */ this.modifiers = Object(); /** * Function to be called after modifier value changes - * * @type {Object.} */ this.callbacks = Object(); } /** * Add a new modifier to controller. - * * @param {string} name Name of modifier */ add(name) { @@ -277,7 +261,6 @@ class HIDModifierList { } /** * Set modifier value - * * @param {string} name Name of modifier * @param {boolean} value Value to be set */ @@ -294,7 +277,6 @@ class HIDModifierList { } /** * Get modifier value - * * @param {string} name Name of modifier * @returns {boolean} Value of modifier */ @@ -307,7 +289,6 @@ class HIDModifierList { } /** * Set modifier callback function - * * @param {string} name Name of reference in HIDModifierList * @param {modifierCallback} callback Function to be called after modifier value changes */ @@ -348,49 +329,42 @@ class HIDPacket { constructor(name, reportId = 0, callback = undefined, header = []) { /** * Name of packet - * * @type {string} */ this.name = name; /** * ReportID of the packet. If the device does not use ReportIDs this must be 0. - * * @type {number} */ this.reportId = reportId; /** * Function to call when the packet type represents an InputReport, and a new report is received. - * * @type {packetCallback} */ this.callback = callback; /** * List of bytes to match from beginning of packet - * * @type {number[]} */ this.header = header; /** * Object of groups, referred by the group string - * * @type {Object.>} */ this.groups = {}; /** * Length of packet in bytes - * * @type {number} */ this.length = this.header.length; /** * Size of the 'pack' types in bytes - * * @type {Object.} */ this.packSizes = {b: 1, B: 1, h: 2, H: 2, i: 4, I: 4}; @@ -400,7 +374,6 @@ class HIDPacket { /** * Pack a field value to the packet. * Can only pack bits and byte values, patches welcome. - * * @todo Implement multi byte bit vector outputs * @param {Uint8Array} data Data to be send as OutputReport to the device * @param {packetField} field Object that describes a field inside of a packet, which can often @@ -458,14 +431,13 @@ class HIDPacket { /** * Parse and return field value matching the 'pack' field from field attributes. * Valid field packing types are: - * - b signed byte - * - B unsigned byte - * - h signed short - * - H unsigned short - * - i signed integer - * - I unsigned integer - * - * @param {Uint8Array} data Data received as InputReport from the device + * - b signed byte + * - B unsigned byte + * - h signed short + * - H unsigned short + * - i signed integer + * - I unsigned integer + * @param {number[] | ArrayBuffer | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array} data Data received as InputReport from the device * @param {packetField} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. * @returns {number} Value for the field in data, represented according the fields packing type @@ -493,9 +465,8 @@ class HIDPacket { /** * Find HID packet group matching name. * Create group if create is true - * * @param {string} name Name of the group - * @param {boolean} [create=false] If true, group will be created + * @param {boolean} [create] If true, group will be created @returns {any} Group Returns group or undefined, when group is not existing and create is set to false */ @@ -514,7 +485,6 @@ class HIDPacket { } /** * Lookup HID packet field matching given offset and pack type - * * @param {number} offset The field's offset from the start of the packet in bytes: * - For HID devices which don't use ReportIDs, the data bytes starts at * position 0 @@ -565,7 +535,6 @@ class HIDPacket { /** * Return a field by group and name from the packet, * Returns undefined if field could not be found - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name e.g. "play" * @returns {packetField} Field @@ -603,7 +572,6 @@ class HIDPacket { } /** * Return reference to a bit in a bitvector field - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name e.g. "play" * @returns {bitObject} Reference to a bit in a bitvector field @@ -635,7 +603,6 @@ class HIDPacket { } /** * Remove a control registered. Normally not needed - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name e.g. "play" */ @@ -652,7 +619,6 @@ class HIDPacket { * * 'group' and 'name' form the ID of the field, if it matches a valid Mixxx control name, * the system attempts to attach it directly to the correct field. - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name e.g. "play" * @param {number} offset The field's offset from the start of the packet in bytes: @@ -667,13 +633,13 @@ class HIDPacket { * - H unsigned short (Uint16 Little-Endian) * - i signed integer (Int32 Little-Endian) * - I unsigned integer (Uint32 Little-Endian) - * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are + * @param {number} [bitmask] A bitwise mask of up to 32 bit. All bits set to'1' in this mask are * considered. * Note: For controls that use full bytes (8bit, 16bit, ...), you can set this to * undefined NOTE: Parsing bitmask with multiple bits is not supported yet. - * @param {boolean} isEncoder indicates if this is an encoder which should be wrapped and delta + * @param {boolean} [isEncoder] indicates if this is an encoder which should be wrapped and delta * reported - * @param {fieldChangeCallback} callback Callback function for the control + * @param {fieldChangeCallback} [callback] Callback function for the control */ addControl(group, name, offset, pack, bitmask, isEncoder, callback) { const control_group = this.getGroup(group, true); @@ -786,13 +752,12 @@ class HIDPacket { /** * Register a Output control field or Output control bit to output packet * Output control field: - * Output field with no bitmask, controls Output with multiple values + * Output field with no bitmask, controls Output with multiple values * Output control bit: - * Output with with bitmask, controls Output with a single bit + * Output with with bitmask, controls Output with a single bit * * It is recommended to define callbacks after packet creation with * setCallback instead of adding it directly here. But you can do it. - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name "VuMeter" * @param {number} offset The field's offset from the start of the packet in bytes: @@ -807,9 +772,9 @@ class HIDPacket { * - H unsigned short (Uint16 Little-Endian) * - i signed integer (Int32 Little-Endian) * - I unsigned integer (Uint32 Little-Endian) - * @param {number} [bitmask=undefined] A bitwise mask of up to 32 bit. All bits set to'1' in this mask are + * @param {number} [bitmask] A bitwise mask of up to 32 bit. All bits set to'1' in this mask are * considered. - * @param {fieldChangeCallback} [callback=undefined] Callback function for the control + * @param {fieldChangeCallback} [callback] Callback function for the control */ addOutput(group, name, offset, pack, bitmask, callback) { const control_group = this.getGroup(group, true); @@ -894,7 +859,6 @@ class HIDPacket { /** * Register a callback to field or a bit vector bit. * Does not make sense for Output fields but you can do that. - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name e.g. "play" * @param {fieldChangeCallback} callback Callback function for the control @@ -929,7 +893,6 @@ class HIDPacket { * This function can be set in script code to ignore a field you don't want to be processed but * still wanted to define, to make packet format complete from specifications. If field is * ignored, it is not reported in 'delta' objects. - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name "pregain" * @param {boolean} ignored 'ignored' flag for field to given value (true or false) @@ -945,7 +908,6 @@ class HIDPacket { /** * Adjust field's minimum delta value. * Input value changes smaller than this are not reported in delta - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name "pregain" * @param {number} mindelta Minimum delta value. @@ -964,7 +926,6 @@ class HIDPacket { } /** * Parse bitvector field values, returning object with the named bits set. - * * @param {packetField} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. * @param {number} value Value must be a valid unsigned byte to parse, with enough bits. @@ -974,7 +935,6 @@ class HIDPacket { parseBitVector(field, value) { /** * Object of bitObjects, referred by a string of group and control name separated by a dot - * * @type {Object.} */ const bits = {}; @@ -1000,14 +960,12 @@ class HIDPacket { * Parse input packet fields from data. * Data is expected to be a Packet() received from HID device. * BitVectors are returned as bits you can iterate separately. - * - * @param {Uint8Array} data Data received as InputReport from the device + * @param {number[] | ArrayBuffer | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array} data Data received as InputReport from the device * @returns {Object.} List of changed fields with new value. */ parse(data) { /** * Object of packetField or bitObjects, referred by a string of group and control name separated by a dot - * * @type {Object.} */ const field_changes = {}; @@ -1083,8 +1041,7 @@ class HIDPacket { * First the header bytes are copied to beginning of packet, then * field object values are packed to the HID packet according to the * field type. - * - * @param {boolean} [debug=false] Enables debug output to console + * @param {boolean} [debug] Enables debug output to console */ send(debug) { const data = new Uint8Array(this.length); @@ -1136,7 +1093,6 @@ class HIDController { /** * - By default 'false' * - Should be set 'true', when controller is found and everything is OK - * * @type {boolean} */ this.initialized = false; @@ -1148,14 +1104,12 @@ class HIDController { /** * HIDPackets representing HID InputReports, by packet name - * * @type {Object.} */ this.InputPackets = {}; /** * HIDPackets representing HID OutputReports, by packet name - * * @type {Object.} */ this.OutputPackets = {}; @@ -1163,7 +1117,6 @@ class HIDController { /** * A map to determine the output Bit or bytewise field by group and name, * across all OutputPackets - * * @type {Map} */ this.OutputFieldLookup = new Map(); @@ -1171,7 +1124,6 @@ class HIDController { /** * Default input packet name: can be modified for controllers * which can swap modes (wiimote for example) - * * @type {string} */ this.defaultPacket = "control"; @@ -1184,7 +1136,6 @@ class HIDController { // override for custom control /** * Set to true, when button 'jog_touch' is active - * * @type {boolean} */ this.isScratchEnabled = false; @@ -1192,7 +1143,6 @@ class HIDController { /** * The resolution of the jogwheel HID control (in intervals per revolution) * - Default is 128 - * * @type {number} */ this.scratchintervalsPerRev = 128; @@ -1200,7 +1150,6 @@ class HIDController { /** * The speed of the imaginary record at 0% pitch - in revolutions per minute (RPM) * - Default 33+1/3 - adjust for comfort - * * @type {number} */ this.scratchRPM = 33 + 1 / 3; @@ -1208,7 +1157,6 @@ class HIDController { /** * The alpha coefficient of the filter * - Default is 1/8 (0.125) - start tune from there - * * @type {number} */ this.scratchAlpha = 1.0 / 8; @@ -1216,7 +1164,6 @@ class HIDController { /** * The beta coefficient of the filter * - Default is scratchAlpha/32 - start tune from there - * * @type {number} */ this.scratchBeta = this.scratchAlpha / 32; @@ -1224,7 +1171,6 @@ class HIDController { /** * - Set 'true' to ramp the deck speed down. * - Set 'false' to stop instantly (default) - * * @type {boolean} */ this.scratchRampOnEnable = false; @@ -1232,14 +1178,12 @@ class HIDController { /** * - Set 'true' to ramp the deck speed up. * - Set 'false' to jump to normal play speed instantly (default) - * * @type {boolean} */ this.scratchRampOnDisable = false; /** * Callback function to call when, jog wheel scratching got enabled or disabled - * * @type {scratchingCallback} */ this.enableScratchCallback = undefined; @@ -1280,7 +1224,6 @@ class HIDController { // /** * Used to map the virtual deck names 'deck', 'deck1' or 'deck2' to actual [ChannelX] - * * @type {string[]} */ this.virtualDecks = ["deck", "deck1", "deck2", "deck3", "deck4"]; @@ -1294,7 +1237,6 @@ class HIDController { * Standard target groups available in mixxx. * * This is used by HID packet parser to recognize group parameters we should try sending to mixxx. - * * @type {string[]} */ this.valid_groups = [ @@ -1328,7 +1270,6 @@ class HIDController { * Set to value in ms to update Outputs periodically * - By default undefined. * - If set, it's a value for timer executed every n ms to update Outputs with updateOutputs() - * * @deprecated This is unused and updateOutputs() doesn't exist - Remove? * @type {number} */ @@ -1336,14 +1277,12 @@ class HIDController { /** * Reference to HIDModifierList object - * * @type {HIDModifierList} */ this.modifiers = new HIDModifierList(); /** * Object of scaling function callbacks by name - * * @type {Object.} */ this.scalers = {}; @@ -1352,14 +1291,12 @@ class HIDController { * Object of engine timer IDs of running auto repeat timers * Key is a user specified timer_id. * Used only in the controller.startAutoRepeatTimer code stubs of Sony-SixxAxis.js and Nintendo-Wiimote.js. - * * @type {Object.} */ this.timers = {}; /** * Auto repeat interval default for fields, where not specified individual (in milliseconds) - * * @default 100 * @type {number} */ @@ -1375,7 +1312,6 @@ class HIDController { /** * Callback that is executed after parsing incoming packet * (see Traktor-Kontrol-F1-scripts.js for an example) - * * @type {packetCallback} */ this.postProcessDelta = undefined; @@ -1395,7 +1331,6 @@ class HIDController { /** * Return deck number from deck name. Deck name can't be virtual deck name * in this function call. - * * @param {string} group Control group name e.g. "[Channel1]" * @returns {number} Number of deck */ @@ -1412,7 +1347,6 @@ class HIDController { } /** * Return the group name from given deck number. - * * @param {number} deck Number of deck * @returns {string} Group name of the deck (e.g. Channel2 for deck number 2) */ @@ -1425,7 +1359,6 @@ class HIDController { /** * Map virtual deck names to real deck group. If group is already * a real mixxx group value, just return it as it without mapping. - * * @param {string} group Control group name e.g. "[Channel1]" * @returns {string} Channel */ @@ -1455,7 +1388,6 @@ class HIDController { } /** * Find Output control matching give group and name - * * @param {string} m_group Mapped group, must be a valid Mixxx control group name e.g. "[Channel1]" * @param {string} m_name Name of mapped control, must be a valid Mixxx control name "VuMeter" * @returns {bitObject|packetField} Bit or bytewise field - Returns undefined if output field @@ -1467,7 +1399,6 @@ class HIDController { /** * Find input packet matching given name. * Returns undefined if input packet name is not registered. - * * @param {string} name Name of packet (it makes sense to refer the HID report type and HID * Report-ID here e.g. 'InputReport_0x02') * @returns {HIDPacket} The input packet @@ -1481,7 +1412,6 @@ class HIDController { /** * Find output packet matching given name * Returns undefined if output packet name is not registered. - * * @param {string} name Name of packet (it makes sense to refer the HID report type and HID * Report-ID here e.g. 'OutputReport_0x81') * @returns {HIDPacket} The output packet @@ -1494,7 +1424,6 @@ class HIDController { } /** * Set input packet callback afterwards - * * @param {string} packet The name of the input packet e.g. 'InputReport_0x02' * @param {packetCallback} callback Callback function for the control */ @@ -1506,7 +1435,6 @@ class HIDController { * Register packet field's callback. * If packet has callback, it is still parsed but no field processing is done, * callback is called directly after unpacking fields from packet. - * * @param {string} packet The name of the input packet e.g. 'InputReport_0x02' * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name "pregain" @@ -1523,7 +1451,6 @@ class HIDController { /** * Register scaling function for a control name * This does not check if given control name is valid - * * @param {string} name Reference of the scaling function in scalers list of HIDController * @param {scalingCallback} callback Scaling function */ @@ -1535,7 +1462,6 @@ class HIDController { } /** * Lookup scaling function for control - * * @param {string} name Reference of the scaling function in scalers list of HIDController * @param {any} _callback Unused * @returns {scalingCallback} Scaling function. Returns undefined if function is not @@ -1549,7 +1475,6 @@ class HIDController { } /** * Change type of a previously defined field to modifier and register it - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name * @param {string} modifier Name of the modifier e.g. 'shiftbutton' @@ -1583,7 +1508,6 @@ class HIDController { /** * Link a previously declared HID control to actual mixxx control - * * @param {string} group Control group name * @param {string} name Control name * @param {string} m_group Mapped group, must be a valid Mixxx control group name e.g. "[Channel1]" @@ -1631,7 +1555,6 @@ class HIDController { * Input packets can be responses from device to queries, or control * data details. The default control data packet must be named in * variable this.defaultPacket to allow automatic processing. - * * @param {HIDPacket} packet The input packet to register */ registerInputPacket(packet) { @@ -1665,7 +1588,6 @@ class HIDController { * This module only supports sending bitvector values and byte fields to device. * If you need other data structures, patches are welcome, or you can just do it * manually in your script without registering the packet. - * * @param {HIDPacket} packet The output packet to register */ registerOutputPacket(packet) { @@ -1701,14 +1623,13 @@ class HIDController { } /** * Parse a packet representing an HID InputReport, and processes each field with "unpack": - * - Calls packet callback and returns, if packet callback was defined - * - Calls processIncomingPacket and processes automated events there. - * - If defined, calls postProcessDelta for results after processing automated fields - * - * @param {Uint8Array} data The data received from an HID InputReport. + * - Calls packet callback and returns, if packet callback was defined + * - Calls processIncomingPacket and processes automated events there. + * - If defined, calls postProcessDelta for results after processing automated fields + * @param {number[] | ArrayBuffer | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array} data The data received from an HID InputReport. * In case of HID devices, which use ReportIDs to enumerate the reports, * the ReportID is stored in the first byte and the data start at the second byte - * @param {number} length Length of the data array in bytes + * @param {number} [length] Length of the data array in bytes */ parsePacket(data, length) { if (this.InputPackets === undefined) { @@ -1773,17 +1694,16 @@ class HIDController { * - Sets modifiers from buttons * - Calls button callbacks, if defined * - Finally tries to run matching engine.setValue() function for buttons - * in default mixxx groups, honoring toggleButtons and other button - * details. Not done if a callback was defined for button. + * in default mixxx groups, honoring toggleButtons and other button + * details. Not done if a callback was defined for button. * * Control (Numeric value) field processing * - Calls scaling functions for control fields, if defined for field. - * Scaling function for encoders (isEncoder attribute is true) scales - * field delta instead of raw value. + * Scaling function for encoders (isEncoder attribute is true) scales + * field delta instead of raw value. * - Calls callback functions for control fields, if defined for field * - Finally tries run matching engine.setValue() function for control - * fields in default mixxx groups. Not done if a callback was defined. - * + * fields in default mixxx groups. Not done if a callback was defined. * @param {any} packet Unused * @param {Object.} delta */ @@ -1812,7 +1732,6 @@ class HIDController { } /** * Get active group for this field - * * @param {packetField|bitObject} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. * @returns {string} Group @@ -1835,7 +1754,6 @@ class HIDController { } /** * Get active control name from field - * * @param {packetField|bitObject} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. * @returns {string} Name of field @@ -1849,7 +1767,6 @@ class HIDController { } /** * Process given button field, triggering events - * * @param {bitObject} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. */ @@ -1920,7 +1837,6 @@ class HIDController { } /** * Process given control field, triggering events - * * @param {packetField} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. */ @@ -1976,7 +1892,6 @@ class HIDController { } /** * Toggle control state from toggle button - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} control Name of the control (button) * @param {number} value Value defined in this.buttonStates @@ -1990,7 +1905,6 @@ class HIDController { } /** * Toggle play/pause state - * * @param {string} group Control group name e.g. "[Channel1]" * @param {packetField} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. @@ -2010,7 +1924,6 @@ class HIDController { * Processing of the 'jog_touch' special button name, which is used to detect * when scratching should be enabled. * Deck is resolved from group with 'resolveDeck' - * * @param {string} group Control group name e.g. "[Channel1]" * @param {boolean} status Enable or Disable scratching: * - true enables scratching (press 'jog_touch' button) @@ -2052,14 +1965,13 @@ class HIDController { * you are warned if following scaling function names are not registered: * * jog - * Scaling function from 'jog_wheel' for rate bend events with mixxx 'jog' - * function. Should return value range suitable for 'jog', whatever you - * wish it to do. + * Scaling function from 'jog_wheel' for rate bend events with mixxx 'jog' + * function. Should return value range suitable for 'jog', whatever you + * wish it to do. * jog_scratch - * Scaling function from 'jog_wheel' for scratch movements with mixxx - * 'scratchTick' function. Should return -1,0,1 or small ranges of integers - * both negative and positive values. - * + * Scaling function from 'jog_wheel' for scratch movements with mixxx + * 'scratchTick' function. Should return -1,0,1 or small ranges of integers + * both negative and positive values. * @param {packetField} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. */ @@ -2102,7 +2014,6 @@ class HIDController { } /** * Stops the specified auto repeat timer - * * @param {string} timer_id Reference of the timer to stop */ stopAutoRepeatTimer(timer_id) { @@ -2115,7 +2026,6 @@ class HIDController { } /** * Toggle field autorepeat on or off - * * @param {string} group * @param {string} name * @param {fieldChangeCallback} callback Callback function for the control @@ -2167,7 +2077,6 @@ class HIDController { } /** * Toggle active deck and update virtual output field control mappings - * * @param {number} deck Number of deck */ switchDeck(deck) { @@ -2250,7 +2159,6 @@ class HIDController { } /** * Link a virtual HID Output to mixxx control - * * @param {string} group Control group name * @param {string} name Control name * @param {string} m_group Mapped group, must be a valid Mixxx control group name e.g. "[Channel1]" @@ -2280,7 +2188,6 @@ class HIDController { } /** * Unlink a virtual HID Output from mixxx control - * * @param {string} group Mixxx control group name e.g. "[Channel1]" * @param {string} name Mixxx control name "VuMeter" * @param {controlCallback} callback Callback function for the control @@ -2303,11 +2210,10 @@ class HIDController { } /** * Set output state to given value - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name "cue_indicator" * @param {number|boolean} value Value to set as new output state of the control - * @param {boolean} [send_packet=false] If true, the packet (an HID OutputReport) is send + * @param {boolean} [send_packet] If true, the packet (an HID OutputReport) is send * immediately */ setOutput(group, name, value, send_packet) { @@ -2324,7 +2230,6 @@ class HIDController { } /** * Set Output to toggle between two values. Reset with setOutput(name,'off') - * * @param {string} group Control group name e.g. "[Channel1]" * @param {string} name Control name "cue_indicator" * @param toggle_value @@ -2345,7 +2250,6 @@ class HIDController { * Don't use 'continue' and 'break' don't work as in normal loops, * because body is a function * 'return' statements in the body function behaves as 'continue' in normal loops - * * @param {Object.} object * @param {function (string):void } body */ From e9de0ed8da3011c23a563d0f9b3056eaf96470a6 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 6 Sep 2023 16:02:42 +0200 Subject: [PATCH 5/9] chore: remove unnecessary ~DlgPrefSound --- src/preferences/dialog/dlgprefsound.cpp | 4 ---- src/preferences/dialog/dlgprefsound.h | 1 - 2 files changed, 5 deletions(-) diff --git a/src/preferences/dialog/dlgprefsound.cpp b/src/preferences/dialog/dlgprefsound.cpp index db6d11d38c0..388a38b0cbf 100644 --- a/src/preferences/dialog/dlgprefsound.cpp +++ b/src/preferences/dialog/dlgprefsound.cpp @@ -244,10 +244,6 @@ DlgPrefSound::DlgPrefSound(QWidget* pParent, MIXXX_WIKI_HARDWARE_COMPATIBILITY_URL))); } -DlgPrefSound::~DlgPrefSound() { - delete m_pLatencyCompensation; -} - /** * Slot called when the preferences dialog is opened or this pane is * selected. diff --git a/src/preferences/dialog/dlgprefsound.h b/src/preferences/dialog/dlgprefsound.h index 8f8b90c8697..b4299297bd4 100644 --- a/src/preferences/dialog/dlgprefsound.h +++ b/src/preferences/dialog/dlgprefsound.h @@ -32,7 +32,6 @@ class DlgPrefSound : public DlgPreferencePage, public Ui::DlgPrefSoundDlg { DlgPrefSound(QWidget* parent, std::shared_ptr soundManager, UserSettingsPointer pSettings); - virtual ~DlgPrefSound(); QUrl helpUrl() const override; From 1917e2b7cb094042d89a45727f354f6b68750579 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 6 Sep 2023 16:07:21 +0200 Subject: [PATCH 6/9] refactor: make AudioInput/-Output::isHidden regular --- src/soundio/soundmanagerutil.cpp | 5 ----- src/soundio/soundmanagerutil.h | 10 +++++++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/soundio/soundmanagerutil.cpp b/src/soundio/soundmanagerutil.cpp index f64cfd8b533..c9ef04a04fc 100644 --- a/src/soundio/soundmanagerutil.cpp +++ b/src/soundio/soundmanagerutil.cpp @@ -312,11 +312,6 @@ QList AudioOutput::getSupportedTypes() { return types; } -bool AudioOutput::isHidden() { - return m_type == RECORD_BROADCAST; -} - - /** * Implements setting the type of an AudioOutput, using * AudioOutput::getSupportedTypes. diff --git a/src/soundio/soundmanagerutil.h b/src/soundio/soundmanagerutil.h index 26f83fc76d0..c5d952911a7 100644 --- a/src/soundio/soundmanagerutil.h +++ b/src/soundio/soundmanagerutil.h @@ -136,7 +136,10 @@ class AudioOutput : public AudioPath { QDomElement toXML(QDomElement *element) const; static AudioOutput fromXML(const QDomElement &xml); static QList getSupportedTypes(); - bool isHidden(); + bool isHidden() const { + return m_type == RECORD_BROADCAST; + } + protected: void setType(AudioPathType type) override; }; @@ -165,6 +168,11 @@ class AudioInput : public AudioPath { QDomElement toXML(QDomElement *element) const; static AudioInput fromXML(const QDomElement &xml); static QList getSupportedTypes(); + // implemented for regularity with AudioOutput + bool isHidden() const { + return false; + } + protected: void setType(AudioPathType type) override; }; From 4e88a21a69ccc69f350923e018b3c17894f86b82 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 6 Sep 2023 16:09:20 +0200 Subject: [PATCH 7/9] feat: make AudioPath ordered, improve operator== impl --- src/soundio/soundmanagerutil.h | 38 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/soundio/soundmanagerutil.h b/src/soundio/soundmanagerutil.h index c5d952911a7..2b25616bfad 100644 --- a/src/soundio/soundmanagerutil.h +++ b/src/soundio/soundmanagerutil.h @@ -102,27 +102,37 @@ class AudioPath { return qHash(path.hashValue(), seed); } - friend bool operator==( - const AudioPath& lhs, - const AudioPath& rhs) { - // Exclude m_channelGroup from comparison! - // See also: hashValue()/qHash() - // TODO: Why?? - return lhs.m_type == rhs.m_type && - lhs.m_index == rhs.m_index; - } + // CppCoreGuidelines C.161: Use non-member functions for symmetric operators + friend constexpr bool operator<(const AudioPath& lhs, + const AudioPath& rhs) noexcept; + friend constexpr bool operator==(const AudioPath& lhs, + const AudioPath& rhs) noexcept; -protected: + protected: virtual void setType(AudioPathType type) = 0; ChannelGroup m_channelGroup; AudioPathType m_type; unsigned char m_index; }; -inline bool operator!=( - const AudioPath& lhs, - const AudioPath& rhs) { - return !(lhs == rhs); +// TODO: turn this into operator<=> once all targets fully support that +// XCode 14 probably and GCC 10 +constexpr bool operator<(const AudioPath& lhs, + const AudioPath& rhs) noexcept { + // Exclude m_channelGroup from comparison! + // See also: hashValue()/qHash() + // TODO: Why?? + return std::tie(lhs.m_type, lhs.m_index) < + std::tie(rhs.m_type, rhs.m_index); +} + +constexpr bool operator==(const AudioPath& lhs, + const AudioPath& rhs) noexcept { + // Exclude m_channelGroup from comparison! + // See also: hashValue()/qHash() + // TODO: Why?? + return std::tie(lhs.m_type, lhs.m_index) == + std::tie(rhs.m_type, rhs.m_index); } /// A source of audio in Mixxx that is to be output to a group of From 62dc17ef73197c9c3a3f9cdc3d9c9531161968aa Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 6 Sep 2023 16:58:50 +0200 Subject: [PATCH 8/9] feat(dlgprefsound): ensure sound item tab order matches visual order --- src/preferences/dialog/dlgprefsound.cpp | 27 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/preferences/dialog/dlgprefsound.cpp b/src/preferences/dialog/dlgprefsound.cpp index 388a38b0cbf..98a7911c784 100644 --- a/src/preferences/dialog/dlgprefsound.cpp +++ b/src/preferences/dialog/dlgprefsound.cpp @@ -2,6 +2,9 @@ #include #include +#include +#include +#include #include "control/controlproxy.h" #include "engine/enginebuffer.h" @@ -308,14 +311,24 @@ QUrl DlgPrefSound::helpUrl() const { * if necessary. */ void DlgPrefSound::initializePaths() { - foreach (AudioOutput out, m_pSoundManager->registeredOutputs()) { - if (!out.isHidden()) { - addPath(out); + // Pre-sort paths so they're added in the order they'll appear later on + // so Tab key order matches order in layout: + // * by AudioPathType + // * identical types by index + auto sortFilterAdd = [this](const QList& l) { + // we use a vec of ref_wrappers since copying the path is unnecessary + // and we really just want to change the order + auto ref_vec_to_sort = std::vector>(l.begin(), l.end()); + std::sort(ref_vec_to_sort.begin(), ref_vec_to_sort.end()); + for (const T& path : ref_vec_to_sort) { + if (!path.isHidden()) { + addPath(path); + } } - } - foreach (AudioInput in, m_pSoundManager->registeredInputs()) { - addPath(in); - } + }; + + sortFilterAdd(m_pSoundManager->registeredOutputs()); + sortFilterAdd(m_pSoundManager->registeredInputs()); } void DlgPrefSound::addPath(const AudioOutput& output) { From d27dc619c5825666592679f80eddecf1b597c599 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 6 Sep 2023 17:01:33 +0200 Subject: [PATCH 9/9] refactor(dlgprefsound): reduce duplication in DlgPrefSound::addPath --- src/preferences/dialog/dlgprefsound.cpp | 77 +++++++++++-------------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/src/preferences/dialog/dlgprefsound.cpp b/src/preferences/dialog/dlgprefsound.cpp index 98a7911c784..3930d0eb8e8 100644 --- a/src/preferences/dialog/dlgprefsound.cpp +++ b/src/preferences/dialog/dlgprefsound.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "control/controlproxy.h" @@ -16,6 +15,23 @@ #include "util/rlimit.h" #include "util/scopedoverridecursor.h" +namespace { + +bool soundItemAlreadyExists(const AudioPath& output, const QWidget& widget) { + for (const QObject* pObj : widget.children()) { + auto item = qobject_cast(pObj); + if (!item || item->type() != output.getType()) { + continue; + } + if (!AudioPath::isIndexed(item->type()) || item->index() == output.getIndex()) { + return true; + } + } + return false; +} + +} // namespace + /** * Construct a new sound preferences pane. Initializes and populates all the * all the controls to the values obtained from SoundManager. @@ -333,29 +349,17 @@ void DlgPrefSound::initializePaths() { void DlgPrefSound::addPath(const AudioOutput& output) { // if we already know about this output, don't make a new entry - foreach (QObject *obj, outputTab->children()) { - DlgPrefSoundItem *item = qobject_cast(obj); - if (item) { - if (item->type() == output.getType()) { - if (AudioPath::isIndexed(item->type())) { - if (item->index() == output.getIndex()) { - return; - } - } else { - return; - } - } - } - } - DlgPrefSoundItem* pSoundItem; - AudioPathType type = output.getType(); - if (AudioPath::isIndexed(type)) { - pSoundItem = new DlgPrefSoundItem( - outputTab, type, m_outputDevices, false, output.getIndex()); - } else { - pSoundItem = new DlgPrefSoundItem(outputTab, type, m_outputDevices, false); + if (soundItemAlreadyExists(output, *outputTab)) { + return; } + AudioPathType type = output.getType(); + // TODO who owns this? + DlgPrefSoundItem* pSoundItem = new DlgPrefSoundItem(outputTab, + type, + m_outputDevices, + false, + AudioPath::isIndexed(type) ? output.getIndex() : 0); connect(this, &DlgPrefSound::refreshOutputDevices, pSoundItem, @@ -367,28 +371,17 @@ void DlgPrefSound::addPath(const AudioOutput& output) { } void DlgPrefSound::addPath(const AudioInput& input) { - DlgPrefSoundItem* pSoundItem; - // if we already know about this input, don't make a new entry - foreach (QObject *obj, inputTab->children()) { - DlgPrefSoundItem *item = qobject_cast(obj); - if (item) { - if (item->type() == input.getType()) { - if (AudioPath::isIndexed(item->type())) { - if (item->index() == input.getIndex()) { - return; - } - } else { - return; - } - } - } + if (soundItemAlreadyExists(input, *inputTab)) { + return; } AudioPathType type = input.getType(); - if (AudioPath::isIndexed(type)) { - pSoundItem = new DlgPrefSoundItem(inputTab, type, m_inputDevices, true, input.getIndex()); - } else { - pSoundItem = new DlgPrefSoundItem(inputTab, type, m_inputDevices, true); - } + // TODO: who owns this? + DlgPrefSoundItem* pSoundItem = new DlgPrefSoundItem(inputTab, + type, + m_inputDevices, + true, + AudioPath::isIndexed(type) ? input.getIndex() : 0); + connect(this, &DlgPrefSound::refreshInputDevices, pSoundItem,