From 95cc2232a05d41d0a4d8e7e2e7bcf1a5bc1889c2 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Tue, 5 Apr 2022 23:59:23 +0200 Subject: [PATCH 01/60] Adapted eslint configuration for common-hid-packet-parser.js Applied eslint changes for common-hid-packet-parser.js --- .eslintrc.json | 8 + res/controllers/common-hid-packet-parser.js | 1014 +++++++++++-------- 2 files changed, 612 insertions(+), 410 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 84911dce953..f70c00d7935 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -71,8 +71,16 @@ "sourceType": "module" } }, + { + "files": ["res/controllers/common-hid-packet-parser.js"], + "globals": { + "console": "readable", + "print": "writable" + } + }, { "files": ["res/controllers/*.js"], + "excludedFiles": "res/controllers/common-hid-packet-parser.js", "globals": { "console": "readable", "svg": "writable", diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 6a47697decc..2df579b3d3d 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1,31 +1,47 @@ /* global controller */ -/** Common HID script debugging function. Just to get logging with 'HID' prefix. */ +/** + * Common HID script debugging function. Just to get logging with 'HID' prefix. + * + * @param message + */ var HIDDebug = function(message) { print("HID " + message); }; -/** HID Bit Vector Class +/** + * HID Bit Vector Class * * Collection of bits in one parsed packet field. These objects are * created by HIDPacket addControl and addOutput and should not be - * created manually. */ + * created manually. + */ var HIDBitVector = function() { this.size = 0; this.bits = {}; }; -/** Return bit offset based on bitmask */ +/** + * Return bit offset based on bitmask + * + * @param bitmask + */ HIDBitVector.prototype.getOffset = function(bitmask) { - for (var i = 0; i < 32; i++) - if ((1 & bitmask >> i) !== 0) - return i; + for (let i = 0; i < 32; i++) { + if ((1 & bitmask >> i) !== 0) { return i; } + } return 0; }; -/** Add a control bitmask to the HIDBitVector */ +/** + * Add a control bitmask to the HIDBitVector + * + * @param group + * @param name + * @param bitmask + */ HIDBitVector.prototype.addBitMask = function(group, name, bitmask) { - var bit = {}; + const bit = {}; bit.type = "button"; bit.packet = undefined; bit.id = group + "." + name; @@ -43,9 +59,15 @@ HIDBitVector.prototype.addBitMask = function(group, name, bitmask) { this.bits[bit.id] = bit; }; -/** Add a Output control bitmask to the HIDBitVector */ +/** + * Add a Output control bitmask to the HIDBitVector + * + * @param group + * @param name + * @param bitmask + */ HIDBitVector.prototype.addOutputMask = function(group, name, bitmask) { - var bit = {}; + const bit = {}; bit.type = "output"; bit.packet = undefined; bit.id = group + "." + name; @@ -61,16 +83,22 @@ HIDBitVector.prototype.addOutputMask = function(group, name, bitmask) { this.bits[bit.id] = bit; }; -/** HID Modifiers object +/** + * HID Modifiers object * * Wraps all defined modifiers to one object with uniform API. - * Don't call directly, this is available as HIDController.modifiers */ + * Don't call directly, this is available as HIDController.modifiers + */ var HIDModifierList = function() { this.modifiers = Object(); this.callbacks = Object(); }; -/** Add a new modifier to controller. */ +/** + * Add a new modifier to controller. + * + * @param name + */ HIDModifierList.prototype.add = function(name) { if (name in this.modifiers) { HIDDebug("Modifier already defined: " + name); @@ -79,7 +107,12 @@ HIDModifierList.prototype.add = function(name) { this.modifiers[name] = undefined; }; -/** Set modifier value */ +/** + * Set modifier value + * + * @param name + * @param value + */ HIDModifierList.prototype.set = function(name, value) { if (!(name in this.modifiers)) { HIDDebug("Unknown modifier: " + name); @@ -87,12 +120,16 @@ HIDModifierList.prototype.set = function(name, value) { } this.modifiers[name] = value; if (name in this.callbacks) { - var callback = this.callbacks[name]; + const callback = this.callbacks[name]; callback(value); } }; -/** Get modifier value */ +/** + * Get modifier value + * + * @param name + */ HIDModifierList.prototype.get = function(name) { if (!(name in this.modifiers)) { HIDDebug("Unknown modifier: " + name); @@ -101,7 +138,12 @@ HIDModifierList.prototype.get = function(name) { return this.modifiers[name]; }; -/** Set modifier callback (update function after modifier state changes) */ +/** + * Set modifier callback (update function after modifier state changes) + * + * @param name + * @param callback + */ HIDModifierList.prototype.setCallback = function(name, callback) { if (!(name in this.modifiers)) { HIDDebug("Unknown modifier: " + name); @@ -114,6 +156,7 @@ HIDModifierList.prototype.setCallback = function(name, callback) { * HID Packet object * * One HID input/output packet to register to HIDController + * * @param name name of packet * @param reportId report ID of the packet. If the device only uses * one report type, this must be 0. @@ -123,7 +166,8 @@ HIDModifierList.prototype.setCallback = function(name, callback) { * callback is not meaningful for output packets * @param header (optional) list of bytes to match from beginning * of packet. Do NOT put the report ID in this; use - * the reportId parameter instead. */ + * the reportId parameter instead. + */ var HIDPacket = function(name, reportId, callback, header) { this.name = name; this.header = header; @@ -141,18 +185,22 @@ var HIDPacket = function(name, reportId, callback, header) { this.signedPackFormats = ["b", "h", "i"]; }; -/** Pack a field value to the packet. - * Can only pack bits and byte values, patches welcome. */ +/** + * Pack a field value to the packet. + * Can only pack bits and byte values, patches welcome. + * + * @param data + * @param field + */ HIDPacket.prototype.pack = function(data, field) { - var value; + let value; if (!(field.pack in this.packSizes)) { HIDDebug("ERROR parsing packed value: invalid pack format " + field.pack); return; } - var bytes = this.packSizes[field.pack]; - var signed = false; - if (this.signedPackFormats.indexOf(field.pack) !== -1) - signed = true; + const bytes = this.packSizes[field.pack]; + let signed = false; + if (this.signedPackFormats.indexOf(field.pack) !== -1) { signed = true; } if (field.type === "bitvector") { // TODO - fix multi byte bit vector outputs @@ -160,8 +208,8 @@ HIDPacket.prototype.pack = function(data, field) { HIDDebug("ERROR: packing multibyte bit vectors not yet supported"); return; } - for (var bit_id in field.value.bits) { - var bit = field.value.bits[bit_id]; + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; data[field.offset] = data[field.offset] | bit.value; } return; @@ -174,8 +222,8 @@ HIDPacket.prototype.pack = function(data, field) { return; } - for (var byte_index = 0; byte_index < bytes; byte_index++) { - var index = field.offset + byte_index; + for (let byte_index = 0; byte_index < bytes; byte_index++) { + const index = field.offset + byte_index; if (signed) { if (value >= 0) { data[index] = (value >> (byte_index * 8)) & 255; @@ -189,108 +237,114 @@ HIDPacket.prototype.pack = function(data, field) { }; -/** Parse and return field value matching the 'pack' field from field attributes. +/** + * Parse and return field value matching the 'pack' field from field attributes. * Valid values are: * b signed byte * B unsigned byte * h signed short * H unsigned short * i signed integer - * I unsigned integer */ + * I unsigned integer + * + * @param data + * @param field + */ HIDPacket.prototype.unpack = function(data, field) { - var value = 0; + let value = 0; if (!(field.pack in this.packSizes)) { HIDDebug("ERROR parsing packed value: invalid pack format " + field.pack); return; } - var bytes = this.packSizes[field.pack]; - var signed = false; - if (this.signedPackFormats.indexOf(field.pack) !== -1) - signed = true; + const bytes = this.packSizes[field.pack]; + let signed = false; + if (this.signedPackFormats.indexOf(field.pack) !== -1) { signed = true; } - for (var field_byte = 0; field_byte < bytes; field_byte++) { - if (data[field.offset + field_byte] === 255 && field_byte === 4) - value += 0; - else - value += data[field.offset + field_byte] * Math.pow(2, (field_byte * 8)); + for (let field_byte = 0; field_byte < bytes; field_byte++) { + if (data[field.offset + field_byte] === 255 && field_byte === 4) { value += 0; } else { value += data[field.offset + field_byte] * Math.pow(2, (field_byte * 8)); } } if (signed) { - var max_value = Math.pow(2, bytes * 8); - var split = max_value / 2 - 1; - if (value > split) - value = value - max_value; + const max_value = Math.pow(2, bytes * 8); + const split = max_value / 2 - 1; + if (value > split) { value = value - max_value; } } return value; }; -/** Find HID packet group matching name. - * Create group if create is true */ +/** + * Find HID packet group matching name. + * Create group if create is true + * + * @param name + * @param create + */ HIDPacket.prototype.getGroup = function(name, create) { - if (this.groups === undefined) - this.groups = {}; - if (name in this.groups) - return this.groups[name]; - if (!create) - return undefined; + if (this.groups === undefined) { this.groups = {}; } + if (name in this.groups) { return this.groups[name]; } + if (!create) { return undefined; } this.groups[name] = {}; return this.groups[name]; }; -/** Lookup HID packet field matching given offset and pack type - * Returns undefined if no patching field can be found. */ +/** + * Lookup HID packet field matching given offset and pack type + * Returns undefined if no patching field can be found. + * + * @param offset + * @param pack + */ HIDPacket.prototype.getFieldByOffset = function(offset, pack) { if (!(pack in this.packSizes)) { HIDDebug("Unknown pack string " + pack); return undefined; } - var end_offset = offset + this.packSizes[pack]; - var group; - var field; - for (var group_name in this.groups) { + const end_offset = offset + this.packSizes[pack]; + let group; + let field; + for (const group_name in this.groups) { group = this.groups[group_name]; - for (var field_id in group) { + for (const field_id in group) { field = group[field_id]; // Same field offset - if (field.offset === offset) - return field; + if (field.offset === offset) { return field; } // 7-8 8-9 // Offset for smaller packet inside multibyte field - if (field.offset < offset && field.end_offset >= end_offset) - return field; + if (field.offset < offset && field.end_offset >= end_offset) { return field; } // Packet offset starts inside field, may overflow - if (field.offset < offset && field.end_offset > offset) - return field; + if (field.offset < offset && field.end_offset > offset) { return field; } // Packet start before field, ends or overflows field - if (field.offset > offset && field.offset < end_offset) - return field; + if (field.offset > offset && field.offset < end_offset) { return field; } } } return undefined; }; -/** Return a field by group and name from the packet, - * Returns undefined if field could not be found */ +/** + * Return a field by group and name from the packet, + * Returns undefined if field could not be found + * + * @param group + * @param name + */ HIDPacket.prototype.getField = function(group, name) { - var field_id = group + "." + name; + const field_id = group + "." + name; if (!(group in this.groups)) { HIDDebug("PACKET " + this.name + " group not found " + group); return undefined; } - var control_group = this.groups[group]; - if (field_id in control_group) - return control_group[field_id]; + let control_group = this.groups[group]; + if (field_id in control_group) { return control_group[field_id]; } // Lookup for bit fields in bitvector matching field name - for (var group_name in this.groups) { + for (const group_name in this.groups) { control_group = this.groups[group_name]; - for (var field_name in control_group) { - var field = control_group[field_name]; - if (field === undefined || field.type !== "bitvector") - continue; - for (var bit_name in field.value.bits) { - var bit = field.value.bits[bit_name]; + for (const field_name in control_group) { + const field = control_group[field_name]; + if (field === undefined || field.type !== "bitvector") { continue; } + for (const bit_name in field.value.bits) { + const bit = field.value.bits[bit_name]; if (bit.id === field_id) { return field; } @@ -301,26 +355,35 @@ HIDPacket.prototype.getField = function(group, name) { return undefined; }; -/** Return reference to a bit in a bitvector field */ +/** + * Return reference to a bit in a bitvector field + * + * @param group + * @param name + */ HIDPacket.prototype.lookupBit = function(group, name) { - var field = this.getField(group, name); + const field = this.getField(group, name); if (field === undefined) { HIDDebug("Bitvector match not found: " + group + "." + name); return undefined; } - var bit_id = group + "." + name; - for (var bit_name in field.value.bits) { - var bit = field.value.bits[bit_name]; - if (bit.id === bit_id) - return bit; + const bit_id = group + "." + name; + for (const bit_name in field.value.bits) { + const bit = field.value.bits[bit_name]; + if (bit.id === bit_id) { return bit; } } HIDDebug("BUG: bit not found after successful field lookup"); return undefined; }; -/** Remove a control registered. Normally not needed */ +/** + * Remove a control registered. Normally not needed + * + * @param group + * @param name + */ HIDPacket.prototype.removeControl = function(group, name) { - var control_group = this.getGroup(group); + const control_group = this.getGroup(group); if (!(name in control_group)) { HIDDebug("Field not in control group " + group + ": " + name); return; @@ -328,7 +391,8 @@ HIDPacket.prototype.removeControl = function(group, name) { delete control_group[name]; }; -/** Register a numeric value to parse from input packet +/** + * Register a numeric value to parse from input packet * * @param group control group name * @param name name of the field @@ -340,8 +404,8 @@ HIDPacket.prototype.removeControl = function(group, name) { * @param callback callback function for the control */ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, isEncoder, callback) { - var control_group = this.getGroup(group, true); - var bitvector = undefined; + const control_group = this.getGroup(group, true); + let bitvector = undefined; if (control_group === undefined) { HIDDebug("ERROR creating HID packet group " + group); return; @@ -351,7 +415,7 @@ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, is return; } - var field = this.getFieldByOffset(offset, pack); + let field = this.getFieldByOffset(offset, pack); if (field !== undefined) { if (bitmask === undefined) { HIDDebug("ERROR registering offset " + offset + " pack " + pack); @@ -390,7 +454,7 @@ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, is field.auto_repeat = undefined; field.auto_repeat_interval = undefined; - var packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); + const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); if (this.signedPackFormats.indexOf(pack) !== -1) { field.min = 0 - (packet_max_value / 2) + 1; field.max = (packet_max_value / 2) - 1; @@ -411,7 +475,7 @@ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, is } // Create a new bitvector field and add the bit to that // TODO - accept controls with bitmask < packet_max_value - var field_name = "bitvector_" + offset; + const field_name = "bitvector_" + offset; field.type = "bitvector"; field.name = field_name; field.id = group + "." + field_name; @@ -436,7 +500,8 @@ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, is } }; -/** Register a Output control field or Output control bit to output packet +/** + * 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 control bit: @@ -445,14 +510,19 @@ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, is * It is recommended to define callbacks after packet creation with * setCallback instead of adding it directly here. But you can do it. * + * @param group + * @param name * @param offset the offset of the byte(s) to use, starting at 1 * (0 is automatically populated with the reportId) - * @param pack control packing format for pack(), one of b/B, h/H, i/I */ + * @param pack control packing format for pack(), one of b/B, h/H, i/I + * @param bitmask + * @param callback + */ HIDPacket.prototype.addOutput = function(group, name, offset, pack, bitmask, callback) { - var control_group = this.getGroup(group, true); - var field; - var bitvector = undefined; - var field_id = group + "." + name; + const control_group = this.getGroup(group, true); + let field; + let bitvector = undefined; + const field_id = group + "." + name; if (control_group === undefined) { return; @@ -493,7 +563,7 @@ HIDPacket.prototype.addOutput = function(group, name, offset, pack, bitmask, cal field.callback = callback; field.toggle = undefined; - var packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); + const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); if (this.signedPackFormats.indexOf(pack) !== -1) { field.min = 0 - (packet_max_value / 2) + 1; field.max = (packet_max_value / 2) - 1; @@ -509,7 +579,7 @@ HIDPacket.prototype.addOutput = function(group, name, offset, pack, bitmask, cal } else { // Create new Output bitvector control field, add bit to it // rewrite name to use bitvector instead - var field_name = "bitvector_" + offset; + const field_name = "bitvector_" + offset; field.type = "bitvector"; field.id = group + "." + field_name; field.name = field_name; @@ -525,11 +595,17 @@ HIDPacket.prototype.addOutput = function(group, name, offset, pack, bitmask, cal control_group[field.id] = field; }; -/** Register a callback to field or a bit vector bit. - * Does not make sense for Output fields but you can do that. */ +/** + * Register a callback to field or a bit vector bit. + * Does not make sense for Output fields but you can do that. + * + * @param group + * @param name + * @param callback + */ HIDPacket.prototype.setCallback = function(group, name, callback) { - var field = this.getField(group, name); - var field_id = group + "." + name; + const field = this.getField(group, name); + const field_id = group + "." + name; if (callback === undefined) { HIDDebug("Callback to add was undefined for " + field_id); return; @@ -540,10 +616,9 @@ HIDPacket.prototype.setCallback = function(group, name, callback) { return; } if (field.type === "bitvector") { - for (var bit_id in field.value.bits) { - var bit = field.value.bits[bit_id]; - if (bit_id !== field_id) - continue; + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; + if (bit_id !== field_id) { continue; } bit.callback = callback; return; } @@ -553,10 +628,16 @@ HIDPacket.prototype.setCallback = function(group, name, callback) { } }; -/** Set 'ignored' flag for field to given value (true or false) - * If field is ignored, it is not reported in 'delta' objects. */ +/** + * Set 'ignored' flag for field to given value (true or false) + * If field is ignored, it is not reported in 'delta' objects. + * + * @param group + * @param name + * @param ignored + */ HIDPacket.prototype.setIgnored = function(group, name, ignored) { - var field = this.getField(group, name); + const field = this.getField(group, name); if (field === undefined) { HIDDebug("ERROR setting ignored flag for " + group + " " + name); return; @@ -564,10 +645,16 @@ HIDPacket.prototype.setIgnored = function(group, name, ignored) { field.ignored = ignored; }; -/** Adjust field's minimum delta value. - * Input value changes smaller than this are not reported in delta */ +/** + * Adjust field's minimum delta value. + * Input value changes smaller than this are not reported in delta + * + * @param group + * @param name + * @param mindelta + */ HIDPacket.prototype.setMinDelta = function(group, name, mindelta) { - var field = this.getField(group, name); + const field = this.getField(group, name); if (field === undefined) { HIDDebug("ERROR adjusting mindelta for " + group + " " + name); return; @@ -579,42 +666,49 @@ HIDPacket.prototype.setMinDelta = function(group, name, mindelta) { field.mindelta = mindelta; }; -/** Parse bitvector field values, returning object with the named bits set. +/** + * Parse bitvector field values, returning object with the named bits set. * Value must be a valid unsigned byte to parse, with enough bits. - * Returns list of modified bits (delta) */ + * Returns list of modified bits (delta) + * + * @param field + * @param value + */ HIDPacket.prototype.parseBitVector = function(field, value) { - var bits = {}; - var bit; - var new_value; - for (var bit_id in field.value.bits) { + const bits = {}; + let bit; + let new_value; + for (const bit_id in field.value.bits) { bit = field.value.bits[bit_id]; new_value = (bit.bitmask & value) >> bit.bit_offset; - if (bit.value !== undefined && bit.value !== new_value) - bits[bit_id] = bit; + if (bit.value !== undefined && bit.value !== new_value) { bits[bit_id] = bit; } bit.value = new_value; } return bits; }; -/** Parse input packet fields from data. +/** + * Parse input packet fields from data. * Data is expected to be a Packet() received from HID device. * Returns list of changed fields with new value. - * BitVectors are returned as bits you can iterate separately. */ + * BitVectors are returned as bits you can iterate separately. + * + * @param data + */ HIDPacket.prototype.parse = function(data) { - var field_changes = {}; - var group; - var group_name; - var field; - var field_id; + const field_changes = {}; + let group; + let group_name; + let field; + let field_id; for (group_name in this.groups) { group = this.groups[group_name]; for (field_id in group) { field = group[field_id]; - if (field === undefined) - continue; + if (field === undefined) { continue; } - var value = this.unpack(data, field); + const value = this.unpack(data, field); if (value === undefined) { HIDDebug("Error parsing packet field value for " + field_id); return; @@ -622,13 +716,11 @@ HIDPacket.prototype.parse = function(data) { if (field.type === "bitvector") { // Bitvector deltas are checked in parseBitVector - var changed_bits = this.parseBitVector(field, value); - for (var bit_name in changed_bits) - field_changes[bit_name] = changed_bits[bit_name]; + const changed_bits = this.parseBitVector(field, value); + for (const bit_name in changed_bits) { field_changes[bit_name] = changed_bits[bit_name]; } } else if (field.type === "control") { - if (field.value === value && field.mindelta !== undefined) - continue; + if (field.value === value && field.mindelta !== undefined) { continue; } if (field.ignored || field.value === undefined) { field.value = value; continue; @@ -660,29 +752,33 @@ HIDPacket.prototype.parse = function(data) { return field_changes; }; -/** Send this HID packet to device. +/** + * Send this HID packet to device. * 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. */ + * field type. + * + * @param debug + */ HIDPacket.prototype.send = function(debug) { - var data = []; + const data = []; if (this.header !== undefined) { - for (var header_byte = 0; header_byte < this.header.length; header_byte++) { + for (let header_byte = 0; header_byte < this.header.length; header_byte++) { data[header_byte] = this.header[header_byte]; } } - for (var group_name in this.groups) { - var group = this.groups[group_name]; - for (var field_name in group) { + for (const group_name in this.groups) { + const group = this.groups[group_name]; + for (const field_name in group) { this.pack(data, group[field_name]); } } if (debug) { - var packet_string = ""; - for (var d in data) { + let packet_string = ""; + for (const d in data) { if (data[d] < 0x10) { // Add padding for bytes smaller than 10 packet_string += "0"; @@ -789,120 +885,147 @@ var HIDController = function() { /** Function to close the controller object cleanly */ HIDController.prototype.close = function() { - for (var name in this.timers) { - var timer = this.timers[name]; - if (timer === undefined) - continue; + for (const name in this.timers) { + const timer = this.timers[name]; + if (timer === undefined) { continue; } engine.stopTimer(timer); this.timers[name] = undefined; } }; -/** Initialize our packet data and callbacks. This does not seem to - * work when executed from here, but we keep a stub just in case. */ +/** + * Initialize our packet data and callbacks. This does not seem to + * work when executed from here, but we keep a stub just in case. + */ HIDController.prototype.initializePacketData = function() { }; -/** Return deck number from deck name. Deck name can't be virtual deck name - * in this function call. */ +/** + * Return deck number from deck name. Deck name can't be virtual deck name + * in this function call. + * + * @param group + */ HIDController.prototype.resolveDeck = function(group) { - if (group === undefined) - return undefined; - var result = group.match(/\[Channel[0-9]+\]/); - if (!result) - return undefined; - var str = group.replace(/\[Channel/, ""); + if (group === undefined) { return undefined; } + const result = group.match(/\[Channel[0-9]+\]/); + if (!result) { return undefined; } + const str = group.replace(/\[Channel/, ""); return str.substring(0, str.length - 1); }; -/** Return the group name from given deck number. */ +/** + * Return the group name from given deck number. + * + * @param deck + */ HIDController.prototype.resolveDeckGroup = function(deck) { - if (deck === undefined) - return undefined; + if (deck === undefined) { return undefined; } return "[Channel" + deck + "]"; }; -/** Map virtual deck names to real deck group. If group is already - * a real mixxx group value, just return it as it without mapping. */ +/** + * 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 group + */ HIDController.prototype.resolveGroup = function(group) { - var channel_name = /\[Channel[0-9]+\]/; - if (group !== undefined && group.match(channel_name)) - return group; + const channel_name = /\[Channel[0-9]+\]/; + if (group !== undefined && group.match(channel_name)) { return group; } if (this.valid_groups.indexOf(group) !== -1) { return group; } if (group === "deck" || group === undefined) { - if (this.activeDeck === undefined) - return undefined; + if (this.activeDeck === undefined) { return undefined; } return "[Channel" + this.activeDeck + "]"; } if (this.activeDeck === 1 || this.activeDeck === 2) { - if (group === "deck1") return "[Channel1]"; - if (group === "deck2") return "[Channel2]"; + if (group === "deck1") { return "[Channel1]"; } + if (group === "deck2") { return "[Channel2]"; } } if (this.activeDeck === 3 || this.activeDeck === 4) { - if (group === "deck1") return "[Channel3]"; - if (group === "deck2") return "[Channel4]"; + if (group === "deck1") { return "[Channel3]"; } + if (group === "deck2") { return "[Channel4]"; } } return undefined; }; -/** Find Output control matching give group and name - * Returns undefined if output field can't be found. */ +/** + * Find Output control matching give group and name + * Returns undefined if output field can't be found. + * + * @param m_group + * @param m_name + */ HIDController.prototype.getOutputField = function(m_group, m_name) { - for (var packet_name in this.OutputPackets) { - var packet = this.OutputPackets[packet_name]; - for (var group_name in packet.groups) { - var group = packet.groups[group_name]; - for (var field_name in group) { - var field = group[field_name]; + for (const packet_name in this.OutputPackets) { + const packet = this.OutputPackets[packet_name]; + for (const group_name in packet.groups) { + const group = packet.groups[group_name]; + for (const field_name in group) { + const field = group[field_name]; if (field.type === "bitvector") { - for (var bit_id in field.value.bits) { - var bit = field.value.bits[bit_id]; - if (bit.mapped_group === m_group && bit.mapped_name === m_name) - return bit; - if (bit.group === m_group && bit.name === m_name) - return bit; + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; + if (bit.mapped_group === m_group && bit.mapped_name === m_name) { return bit; } + if (bit.group === m_group && bit.name === m_name) { return bit; } } continue; } - if (field.mapped_group === m_group && field.mapped_name === m_name) - return field; - if (field.group === m_group && field.name === m_name) - return field; + if (field.mapped_group === m_group && field.mapped_name === m_name) { return field; } + if (field.group === m_group && field.name === m_name) { return field; } } } } return undefined; }; -/** Find input packet matching given name. - * Returns undefined if input packet name is not registered. */ +/** + * Find input packet matching given name. + * Returns undefined if input packet name is not registered. + * + * @param name + */ HIDController.prototype.getInputPacket = function(name) { - if (!(name in this.InputPackets)) - return undefined; + if (!(name in this.InputPackets)) { return undefined; } return this.InputPackets[name]; }; -/** Find output packet matching given name - * Returns undefined if output packet name is not registered. */ +/** + * Find output packet matching given name + * Returns undefined if output packet name is not registered. + * + * @param name + */ HIDController.prototype.getOutputPacket = function(name) { - if (!(name in this.OutputPackets)) - return undefined; + if (!(name in this.OutputPackets)) { return undefined; } return this.OutputPackets[name]; }; -/** Set input packet callback afterwards */ +/** + * Set input packet callback afterwards + * + * @param packet + * @param callback + */ HIDController.prototype.setPacketCallback = function(packet, callback) { - var input_packet = this.getInputPacket(packet); + const input_packet = this.getInputPacket(packet); input_packet.callback = callback; }; -/** Register packet field's callback. +/** + * 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. */ + * callback is called directly after unpacking fields from packet. + * + * @param packet + * @param group + * @param name + * @param callback + */ HIDController.prototype.setCallback = function(packet, group, name, callback) { - var input_packet = this.getInputPacket(packet); + const input_packet = this.getInputPacket(packet); if (input_packet === undefined) { HIDDebug("Input packet not found " + packet); return; @@ -910,33 +1033,47 @@ HIDController.prototype.setCallback = function(packet, group, name, callback) { input_packet.setCallback(group, name, callback); }; -/** Register scaling function for a control name - * This does not check if given control name is valid */ +/** + * Register scaling function for a control name + * This does not check if given control name is valid + * + * @param name + * @param callback + */ HIDController.prototype.setScaler = function(name, callback) { - if (name in this.scalers) - return; + if (name in this.scalers) { return; } this.scalers[name] = callback; }; -/** Lookup scaling function for control - * Returns undefined if function is not registered. */ +/** + * Lookup scaling function for control + * Returns undefined if function is not registered. + * + * @param name + * @param _callback + */ HIDController.prototype.getScaler = function(name, _callback) { - if (!(name in this.scalers)) - return undefined; + if (!(name in this.scalers)) { return undefined; } return this.scalers[name]; }; -/** Change type of a previously defined field to modifier and register it */ +/** + * Change type of a previously defined field to modifier and register it + * + * @param group + * @param name + * @param modifier + */ HIDController.prototype.linkModifier = function(group, name, modifier) { - var packet = this.getInputPacket(this.defaultPacket); + const packet = this.getInputPacket(this.defaultPacket); if (packet === undefined) { HIDDebug( "ERROR creating modifier: input packet " + this.defaultPacket + " not found" ); return; } - var bit_id = group + "." + name; - var field = packet.lookupBit(group, name); + const bit_id = group + "." + name; + const field = packet.lookupBit(group, name); if (field === undefined) { HIDDebug("BIT field not found: " + bit_id); return; @@ -946,15 +1083,29 @@ HIDController.prototype.linkModifier = function(group, name, modifier) { this.modifiers.set(modifier); }; -/** TODO - implement unlinking of modifiers */ +/** + * TODO - implement unlinking of modifiers + * + * @param _group + * @param _name + * @param _modifier + */ HIDController.prototype.unlinkModifier = function(_group, _name, _modifier) { HIDDebug("Unlinking of modifiers not yet implemented"); }; -/** Link a previously declared HID control to actual mixxx control */ +/** + * Link a previously declared HID control to actual mixxx control + * + * @param group + * @param name + * @param m_group + * @param m_name + * @param callback + */ HIDController.prototype.linkControl = function(group, name, m_group, m_name, callback) { - var field; - var packet = this.getInputPacket(this.defaultPacket); + let field; + const packet = this.getInputPacket(this.defaultPacket); if (packet === undefined) { HIDDebug("ERROR creating modifier: input packet " + this.defaultPacket + " not found"); return; @@ -973,59 +1124,69 @@ HIDController.prototype.linkControl = function(group, name, m_group, m_name, cal } field.mapped_group = m_group; field.mapped_name = m_name; - if (callback !== undefined) - field.callback = callback; + if (callback !== undefined) { field.callback = callback; } }; -/** TODO - implement unlinking of controls */ +/** + * TODO - implement unlinking of controls + * + * @param _group + * @param _name + */ HIDController.prototype.unlinkControl = function(_group, _name) { }; -/** Register HID input packet type to controller. +/** + * Register HID input packet type to controller. * 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. */ + * variable this.defaultPacket to allow automatic processing. + * + * @param packet + */ HIDController.prototype.registerInputPacket = function(packet) { // Find modifiers and other special cases from packet fields - for (var group_name in packet.groups) { - var group = packet.groups[group_name]; - for (var field_name in group) { - var field = group[field_name]; + for (const group_name in packet.groups) { + const group = packet.groups[group_name]; + for (const field_name in group) { + const field = group[field_name]; field.packet = packet; if (field.type === "bitvector") { - for (var bit_id in field.value.bits) { - var bit = field.value.bits[bit_id]; + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; bit.packet = packet; - if (bit.group === "modifiers") - this.modifiers.add(bit.name); + if (bit.group === "modifiers") { this.modifiers.add(bit.name); } } } else { - if (field.group === "modifiers") - this.modifiers.add(field.name); + if (field.group === "modifiers") { this.modifiers.add(field.name); } } } } this.InputPackets[packet.name] = packet; }; -/** Register HID output packet type to controller +/** + * Register HID output packet type to controller * There are no special Output control output packets, just register Outputs to any * valid packet and we detect them here. * 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. */ + * manually in your script without registering the packet. + * + * @param packet + */ HIDController.prototype.registerOutputPacket = function(packet) { this.OutputPackets[packet.name] = packet; // Link packet to all fields - for (var group_name in packet.groups) { - var group = packet.groups[group_name]; - for (var field_name in group) { - var field = group[field_name]; + for (const group_name in packet.groups) { + const group = packet.groups[group_name]; + for (const field_name in group) { + const field = group[field_name]; field.packet = packet; if (field.type === "bitvector") { - for (var bit_id in field.value.bits) { - var bit = field.value.bits[bit_id]; + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; bit.packet = packet; } } @@ -1033,17 +1194,22 @@ HIDController.prototype.registerOutputPacket = function(packet) { } }; -/** Parse a received input packet fields with "unpack" calls to fields +/** + * Parse a received input packet fields with "unpack" calls to fields * Calls packet callback and returns, if packet callback was defined * Calls processIncomingPacket and processes automated events there. - * If defined, calls processDelta for results after processing automated fields */ + * If defined, calls processDelta for results after processing automated fields + * + * @param data + * @param length + */ HIDController.prototype.parsePacket = function(data, length) { - var packet; - var changed_data; + let packet; + let changed_data; if (this.InputPackets === undefined) { return; } - for (var name in this.InputPackets) { + for (const name in this.InputPackets) { packet = this.InputPackets[name]; // When the device uses multiple report types with report IDs, hidapi @@ -1056,14 +1222,13 @@ HIDController.prototype.parsePacket = function(data, length) { } if (packet.header !== undefined) { - for (var header_byte = 0; header_byte < packet.header.length; header_byte++) { + for (let header_byte = 0; header_byte < packet.header.length; header_byte++) { if (packet.header[header_byte] !== data[header_byte]) { packet = undefined; break; } } - if (packet === undefined) - continue; + if (packet === undefined) { continue; } } changed_data = packet.parse(data); if (packet.callback !== undefined) { @@ -1071,20 +1236,18 @@ HIDController.prototype.parsePacket = function(data, length) { return; } // Process named group controls - if (packet.name === this.defaultPacket) - this.processIncomingPacket(packet, changed_data); + if (packet.name === this.defaultPacket) { this.processIncomingPacket(packet, changed_data); } // Process generic changed_data packet, if callback is defined - if (this.processDelta !== undefined) - this.processDelta(packet, changed_data); - if (this.postProcessDelta !== undefined) - this.postProcessDelta(packet, changed_data); + if (this.processDelta !== undefined) { this.processDelta(packet, changed_data); } + if (this.postProcessDelta !== undefined) { this.postProcessDelta(packet, changed_data); } return; } HIDDebug("Received unknown packet of " + length + " bytes"); - for (var i in data) HIDDebug("BYTE " + data[i]); + for (const i in data) { HIDDebug("BYTE " + data[i]); } }; -/** Process the modified field values (delta) from input packet fields for +/** + * Process the modified field values (delta) from input packet fields for * input control packet, if packet name is in this.defaultPacket. * * Button field processing: @@ -1100,32 +1263,33 @@ HIDController.prototype.parsePacket = function(data, length) { * 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 packet + * @param delta + */ HIDController.prototype.processIncomingPacket = function(packet, delta) { - var field; - for (var name in delta) { + let field; + for (const name in delta) { if (this.ignoredControlChanges !== undefined - && this.ignoredControlChanges.indexOf(name) !== -1) - continue; + && this.ignoredControlChanges.indexOf(name) !== -1) { continue; } field = delta[name]; - if (field.type === "button") - this.processButton(field); - else if (field.type === "control") - this.processControl(field); - else - HIDDebug("Unknown field " + field.name + " type " + field.type); + if (field.type === "button") { this.processButton(field); } else if (field.type === "control") { this.processControl(field); } else { HIDDebug("Unknown field " + field.name + " type " + field.type); } } }; -/** Get active group for this field */ +/** + * Get active group for this field + * + * @param field + */ HIDController.prototype.getActiveFieldGroup = function(field) { if (field.mapped_group !== undefined) { return this.resolveGroup(field.mapped_group); } - var group = field.group; + const group = field.group; if (group === undefined) { - if (this.activeDeck !== undefined) - return "[Channel" + this.activeDeck + "]"; + if (this.activeDeck !== undefined) { return "[Channel" + this.activeDeck + "]"; } } if (this.valid_groups.indexOf(group) !== -1) { //HIDDebug("Resolving group " + group); @@ -1134,17 +1298,24 @@ HIDController.prototype.getActiveFieldGroup = function(field) { return group; }; -/** Get active control name from field */ +/** + * Get active control name from field + * + * @param field + */ HIDController.prototype.getActiveFieldControl = function(field) { - if (field.mapped_name !== undefined) - return field.mapped_name; + if (field.mapped_name !== undefined) { return field.mapped_name; } return field.name; }; -/** Process given button field, triggering events */ +/** + * Process given button field, triggering events + * + * @param field + */ HIDController.prototype.processButton = function(field) { - var group = this.getActiveFieldGroup(field); - var control = this.getActiveFieldControl(field); + const group = this.getActiveFieldGroup(field); + const control = this.getActiveFieldControl(field); if (group === undefined) { HIDDebug("processButton: Could not resolve group from " @@ -1155,14 +1326,11 @@ HIDController.prototype.processButton = function(field) { } if (group === "modifiers") { - if (field.value !== 0) - this.modifiers.set(control, true); - else - this.modifiers.set(control, false); + if (field.value !== 0) { this.modifiers.set(control, true); } else { this.modifiers.set(control, false); } return; } if (field.auto_repeat) { - var timer_id = "auto_repeat_" + field.id; + const timer_id = "auto_repeat_" + field.id; if (field.value) { this.startAutoRepeatTimer(timer_id, field.auto_repeat_interval); } else { @@ -1175,21 +1343,14 @@ HIDController.prototype.processButton = function(field) { } if (control === "jog_touch") { if (group !== undefined) { - if (field.value === this.buttonStates.pressed) - this.enableScratch(group, true); - else - this.enableScratch(group, false); + if (field.value === this.buttonStates.pressed) { this.enableScratch(group, true); } else { this.enableScratch(group, false); } } return; } if (this.toggleButtons.indexOf(control) !== -1) { - if (field.value === this.buttonStates.released) - return; + if (field.value === this.buttonStates.released) { return; } if (engine.getValue(group, control)) { - if (control === "play") - engine.setValue(group, "stop", true); - else - engine.setValue(group, control, false); + if (control === "play") { engine.setValue(group, "stop", true); } else { engine.setValue(group, control, false); } } else { engine.setValue(group, control, true); } @@ -1205,11 +1366,15 @@ HIDController.prototype.processButton = function(field) { } }; -/** Process given control field, triggering events */ +/** + * Process given control field, triggering events + * + * @param field + */ HIDController.prototype.processControl = function(field) { - var value; - var group = this.getActiveFieldGroup(field); - var control = this.getActiveFieldControl(field); + let value; + const group = this.getActiveFieldGroup(field); + const control = this.getActiveFieldControl(field); if (group === undefined) { HIDDebug("processControl: Could not resolve group from " @@ -1234,11 +1399,10 @@ HIDController.prototype.processControl = function(field) { } // Call value scaler if defined and send mixxx signal value = field.value; - var scaler = this.getScaler(control); + const scaler = this.getScaler(control); if (field.isEncoder) { - var field_delta = field.delta; - if (scaler !== undefined) - field_delta = scaler(group, control, field_delta); + let field_delta = field.delta; + if (scaler !== undefined) { field_delta = scaler(group, control, field_delta); } engine.setValue(group, control, field_delta); } else { if (scaler !== undefined) { @@ -1255,26 +1419,33 @@ HIDController.prototype.processControl = function(field) { } }; -/** Toggle control state from toggle button */ +/** + * Toggle control state from toggle button + * + * @param group + * @param control + * @param value + */ HIDController.prototype.toggle = function(group, control, value) { - if (value === this.buttonStates.released) - return; - var status = engine.getValue(group, control) !== true; + if (value === this.buttonStates.released) { return; } + const status = engine.getValue(group, control) !== true; engine.setValue(group, control, status); }; -/** Toggle play/pause state */ +/** + * Toggle play/pause state + * + * @param group + * @param field + */ HIDController.prototype.togglePlay = function(group, field) { - if (field.value === this.buttonStates.released) - return; - var status = !(engine.getValue(group, "play")); - if (!status) - engine.setValue(group, "stop", true); - else - engine.setValue(group, "play", true); + if (field.value === this.buttonStates.released) { return; } + const status = !(engine.getValue(group, "play")); + if (!status) { engine.setValue(group, "stop", true); } else { engine.setValue(group, "play", true); } }; -/** Processing of the 'jog_touch' special button name, which is used to detect +/** + * 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' * @@ -1284,9 +1455,13 @@ HIDController.prototype.togglePlay = function(group, field) { * * Disabling scratching (release 'jog_touch' button) * Sets the internal 'isScratchEnabled attribute to false, and calls scratchDisable - * to end scratching mode */ + * to end scratching mode + * + * @param group + * @param status + */ HIDController.prototype.enableScratch = function(group, status) { - var deck = this.resolveDeck(group); + const deck = this.resolveDeck(group); if (status) { this.isScratchEnabled = true; engine.scratchEnable(deck, @@ -1296,15 +1471,16 @@ HIDController.prototype.enableScratch = function(group, status) { this.scratchBeta, this.rampedScratchEnable ); - if (this.enableScratchCallback !== undefined) this.enableScratchCallback(true); + if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(true); } } else { this.isScratchEnabled = false; engine.scratchDisable(deck, this.rampedScratchDisable); - if (this.enableScratchCallback !== undefined) this.enableScratchCallback(false); + if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(false); } } }; -/** Default jog scratching function. Used to handle jog move events from special +/** + * Default jog scratching function. Used to handle jog move events from special * input control field called 'jog_wheel'. Handles both 'scratch' and 'jog' mixxx * functions, depending on isScratchEnabled value above (see enableScratch()) * @@ -1319,33 +1495,24 @@ HIDController.prototype.enableScratch = function(group, status) { * 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 field */ HIDController.prototype.jog_wheel = function(field) { - var scaler = undefined; - var active_group = this.getActiveFieldGroup(field); - var value = undefined; - if (field.isEncoder) - value = field.delta; - else - value = field.value; + let scaler = undefined; + const active_group = this.getActiveFieldGroup(field); + let value = undefined; + if (field.isEncoder) { value = field.delta; } else { value = field.value; } if (this.isScratchEnabled) { - var deck = this.resolveDeck(active_group); - if (deck === undefined) - return; + const deck = this.resolveDeck(active_group); + if (deck === undefined) { return; } scaler = this.getScaler("jog_scratch"); - if (scaler !== undefined) - value = scaler(active_group, "jog_scratch", value); - else - HIDDebug("WARNING non jog_scratch scaler, you likely want one"); + if (scaler !== undefined) { value = scaler(active_group, "jog_scratch", value); } else { HIDDebug("WARNING non jog_scratch scaler, you likely want one"); } engine.scratchTick(deck, value); } else { - if (active_group === undefined) - return; + if (active_group === undefined) { return; } scaler = this.getScaler("jog"); - if (scaler !== undefined) - value = scaler(active_group, "jog", value); - else - HIDDebug("WARNING non jog scaler, you likely want one"); + if (scaler !== undefined) { value = scaler(active_group, "jog", value); } else { HIDDebug("WARNING non jog scaler, you likely want one"); } engine.setValue(active_group, "jog", value); } }; @@ -1359,58 +1526,65 @@ HIDController.prototype.stopAutoRepeatTimer = function(timer_id) { } }; -/** Toggle field autorepeat on or off */ +/** + * Toggle field autorepeat on or off + * + * @param group + * @param name + * @param callback + * @param interval + */ HIDController.prototype.setAutoRepeat = function(group, name, callback, interval) { - var packet = this.getInputPacket(this.defaultPacket); - var field = packet.getField(group, name); + const packet = this.getInputPacket(this.defaultPacket); + const field = packet.getField(group, name); if (field === undefined) { HIDDebug("setAutoRepeat: field not found " + group + "." + name); return; } field.auto_repeat = callback; - if (interval) - field.auto_repeat_interval = interval; - else - field.auto_repeat_interval = controller.auto_repeat_interval; - if (callback) - callback(field); + if (interval) { field.auto_repeat_interval = interval; } else { field.auto_repeat_interval = controller.auto_repeat_interval; } + if (callback) { callback(field); } }; -/** Callback for auto repeat timer to send again the values for +/** + * Callback for auto repeat timer to send again the values for * buttons and controls marked as 'auto_repeat' * Timer must be defined from actual controller side, because of - * callback call namespaces and 'this' reference */ + * callback call namespaces and 'this' reference + */ HIDController.prototype.autorepeatTimer = function() { - var group_name; - var group; - var field; - var field_name; - var bit_name; - var bit; - var packet = this.InputPackets[this.defaultPacket]; + let group_name; + let group; + let field; + let field_name; + let bit_name; + let bit; + const packet = this.InputPackets[this.defaultPacket]; for (group_name in packet.groups) { group = packet.groups[group_name]; for (field_name in group) { field = group[field_name]; if (field.type !== "bitvector") { - if (field.auto_repeat) - this.processControl(field); + if (field.auto_repeat) { this.processControl(field); } continue; } for (bit_name in field.value.bits) { bit = field.value.bits[bit_name]; - if (bit.auto_repeat) - this.processButton(bit); + if (bit.auto_repeat) { this.processButton(bit); } } } } }; -/** Toggle active deck and update virtual output field control mappings */ +/** + * Toggle active deck and update virtual output field control mappings + * + * @param deck + */ HIDController.prototype.switchDeck = function(deck) { - var packet; - var field; - var controlgroup; + let packet; + let field; + let controlgroup; if (deck === undefined) { if (this.activeDeck === undefined) { deck = 1; @@ -1419,72 +1593,77 @@ HIDController.prototype.switchDeck = function(deck) { // var totalDecks = engine.getValue("[Master]","num_decks"); // deck = (this.activeDeck+1) % totalDecks; deck = this.deckSwitchMap[this.activeDeck]; - if (deck === undefined) - deck = 1; + if (deck === undefined) { deck = 1; } } } - var new_group = this.resolveDeckGroup(deck); + const new_group = this.resolveDeckGroup(deck); HIDDebug("Switching to deck " + deck + " group " + new_group); - if (this.disconnectDeck !== undefined) - this.disconnectDeck(); - for (var packet_name in this.OutputPackets) { + if (this.disconnectDeck !== undefined) { this.disconnectDeck(); } + for (const packet_name in this.OutputPackets) { packet = this.OutputPackets[packet_name]; - for (var group_name in packet.groups) { - var group = packet.groups[group_name]; - for (var field_name in group) { + for (const group_name in packet.groups) { + const group = packet.groups[group_name]; + for (const field_name in group) { field = group[field_name]; if (field.type === "bitvector") { - for (var bit_id in field.value.bits) { - var bit = field.value.bits[bit_id]; - if (this.virtualDecks.indexOf(bit.mapped_group) === -1) - continue; + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; + if (this.virtualDecks.indexOf(bit.mapped_group) === -1) { continue; } controlgroup = this.resolveGroup(bit.mapped_group); engine.connectControl(controlgroup, bit.mapped_name, bit.mapped_callback, true); engine.connectControl(new_group, bit.mapped_name, bit.mapped_callback); var value = engine.getValue(new_group, bit.mapped_name); HIDDebug("BIT " + bit.group + "." + bit.name + " value " + value); - if (value) + if (value) { this.setOutput( bit.group, bit.name, this.LEDColors[this.deckOutputColors[deck]] ); - else + } else { this.setOutput( bit.group, bit.name, this.LEDColors.off ); + } } continue; } // Only move outputs of virtual decks - if (this.virtualDecks.indexOf(field.mapped_group) === -1) - continue; + if (this.virtualDecks.indexOf(field.mapped_group) === -1) { continue; } controlgroup = this.resolveGroup(field.mapped_group); engine.connectControl(controlgroup, field.mapped_name, field.mapped_callback, true); engine.connectControl(new_group, field.mapped_name, field.mapped_callback); value = engine.getValue(new_group, field.mapped_name); - if (value) + if (value) { this.setOutput( field.group, field.name, this.LEDColors[this.deckOutputColors[deck]] ); - else + } else { this.setOutput( field.group, field.name, this.LEDColors.off ); + } } } } this.activeDeck = deck; - if (this.connectDeck !== undefined) - this.connectDeck(); + if (this.connectDeck !== undefined) { this.connectDeck(); } }; -/** Link a virtual HID Output to mixxx control */ +/** + * Link a virtual HID Output to mixxx control + * + * @param group + * @param name + * @param m_group + * @param m_name + * @param callback + */ HIDController.prototype.linkOutput = function(group, name, m_group, m_name, callback) { - var controlgroup; - var field = this.getOutputField(group, name); + let controlgroup; + const field = this.getOutputField(group, name); if (field === undefined) { HIDDebug("Linked output not found: " + group + "." + name); return; @@ -1498,16 +1677,19 @@ HIDController.prototype.linkOutput = function(group, name, m_group, m_name, call field.mapped_name = m_name; field.mapped_callback = callback; engine.connectControl(controlgroup, m_name, callback); - if (engine.getValue(controlgroup, m_name)) - this.setOutput(m_group, m_name, "on"); - else - this.setOutput(m_group, m_name, "off"); + if (engine.getValue(controlgroup, m_name)) { this.setOutput(m_group, m_name, "on"); } else { this.setOutput(m_group, m_name, "off"); } }; -/** Unlink a virtual HID Output from mixxx control */ +/** + * Unlink a virtual HID Output from mixxx control + * + * @param group + * @param name + * @param callback + */ HIDController.prototype.unlinkOutput = function(group, name, callback) { - var field = this.getOutputField(group, name); - var controlgroup; + const field = this.getOutputField(group, name); + let controlgroup; if (field === undefined) { HIDDebug("Unlinked output not found: " + group + "." + name); return; @@ -1523,22 +1705,34 @@ HIDController.prototype.unlinkOutput = function(group, name, callback) { field.mapped_callback = undefined; }; -/** Set output state to given value */ +/** + * Set output state to given value + * + * @param group + * @param name + * @param value + * @param send_packet + */ HIDController.prototype.setOutput = function(group, name, value, send_packet) { - var field = this.getOutputField(group, name); + const field = this.getOutputField(group, name); if (field === undefined) { HIDDebug("setOutput: unknown field: " + group + "." + name); return; } field.value = value << field.bit_offset; field.toggle = value << field.bit_offset; - if (send_packet) - field.packet.send(); + if (send_packet) { field.packet.send(); } }; -/** Set Output to toggle between two values. Reset with setOutput(name,'off') */ +/** + * Set Output to toggle between two values. Reset with setOutput(name,'off') + * + * @param group + * @param name + * @param toggle_value + */ HIDController.prototype.setOutputToggle = function(group, name, toggle_value) { - var field = this.getOutputField(group, name); + const field = this.getOutputField(group, name); if (field === undefined) { HIDDebug("setOutputToggle: unknown field " + group + "." + name); return; From e1df6de349d625547e6dbd9f991221456b1a3dd0 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Mon, 18 Apr 2022 18:00:39 +0200 Subject: [PATCH 02/60] Added and improved JSDOC documentation --- res/controllers/common-hid-packet-parser.js | 416 ++++++++++++-------- 1 file changed, 251 insertions(+), 165 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 2df579b3d3d..06ec35e8b92 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -3,7 +3,7 @@ /** * Common HID script debugging function. Just to get logging with 'HID' prefix. * - * @param message + * @param {string} message Message to be printed on controller debug console output */ var HIDDebug = function(message) { print("HID " + message); @@ -22,9 +22,10 @@ var HIDBitVector = function() { }; /** - * Return bit offset based on bitmask + * Get the index of the least significant bit that is 1 in `bitmask` * - * @param 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` */ HIDBitVector.prototype.getOffset = function(bitmask) { for (let i = 0; i < 32; i++) { @@ -36,9 +37,9 @@ HIDBitVector.prototype.getOffset = function(bitmask) { /** * Add a control bitmask to the HIDBitVector * - * @param group - * @param name - * @param bitmask + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. */ HIDBitVector.prototype.addBitMask = function(group, name, bitmask) { const bit = {}; @@ -62,9 +63,9 @@ HIDBitVector.prototype.addBitMask = function(group, name, bitmask) { /** * Add a Output control bitmask to the HIDBitVector * - * @param group - * @param name - * @param bitmask + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. */ HIDBitVector.prototype.addOutputMask = function(group, name, bitmask) { const bit = {}; @@ -97,7 +98,7 @@ var HIDModifierList = function() { /** * Add a new modifier to controller. * - * @param name + * @param {string} name Name of modifier */ HIDModifierList.prototype.add = function(name) { if (name in this.modifiers) { @@ -110,8 +111,8 @@ HIDModifierList.prototype.add = function(name) { /** * Set modifier value * - * @param name - * @param value + * @param {string} name Name of modifier + * @param {number} value Value to be set */ HIDModifierList.prototype.set = function(name, value) { if (!(name in this.modifiers)) { @@ -128,7 +129,8 @@ HIDModifierList.prototype.set = function(name, value) { /** * Get modifier value * - * @param name + * @param {string} name Name of modifier + * @returns {number} Value of modifier */ HIDModifierList.prototype.get = function(name) { if (!(name in this.modifiers)) { @@ -138,11 +140,20 @@ HIDModifierList.prototype.get = function(name) { return this.modifiers[name]; }; +/** + * Callback function to call when, the packet represents an HID InputReport, and new data for this 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 {number[]} changed_data The data received from the device + */ + /** * Set modifier callback (update function after modifier state changes) * - * @param name - * @param callback + * @param {string} name Name of reference in HIDModifierList + * @param {packetCallback} callback Function to be called when value changed */ HIDModifierList.prototype.setCallback = function(name, callback) { if (!(name in this.modifiers)) { @@ -155,16 +166,17 @@ HIDModifierList.prototype.setCallback = function(name, callback) { /** * HID Packet object * - * One HID input/output packet to register to HIDController + * An HIDPacket represents one HID report of type InputReport or OutputReport (FeatureReports are currently not supported) + * + * Each HIDPacket must be registered to HIDController. * - * @param name name of packet - * @param reportId report ID of the packet. If the device only uses + * @param {string} name Name of packet (it makes sense to refer the HID report type and HID Report-ID here e.g. 'InputReport_0x02' or 'OutputReport_0x81') + * @param {number} reportId report ID of the packet. If the device only uses * one report type, this must be 0. - * @param callback function to call when this packet type is input - * and is received. If packet callback is set, the + * @param {packetCallback} callback function to call when the packet type represents an InputReport an a new report is received. If packet callback is set, the * packet is not parsed by delta functions. * callback is not meaningful for output packets - * @param header (optional) list of bytes to match from beginning + * @param {number[]} header (optional) list of bytes to match from beginning * of packet. Do NOT put the report ID in this; use * the reportId parameter instead. */ @@ -185,12 +197,33 @@ var HIDPacket = function(name, reportId, callback, header) { this.signedPackFormats = ["b", "h", "i"]; }; +/** + * @typedef packetField + * @type {object} + * @property {HIDPacket} packet + * @property {string} id Group and control name separated by a dot + * @property {string} group + * @property {string} name + * @property {string} mapped_group + * @property {string} mapped_name + * @property {object} pack Control packing format for unpack(), one of b/B, h/H, i/I + * @property {number} offset + * @property {number} end_offset + * @property {number} bitmask + * @property {boolean} isEncoder + * @property {controlCallback} callback + * @property {boolean} soft_takeover + * @property {boolean} ignored + * @property {boolean} auto_repeat + * @property {number} auto_repeat_interval + */ + /** * Pack a field value to the packet. * Can only pack bits and byte values, patches welcome. * - * @param data - * @param field + * @param {number} 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. */ HIDPacket.prototype.pack = function(data, field) { let value; @@ -239,16 +272,17 @@ HIDPacket.prototype.pack = function(data, field) { /** * Parse and return field value matching the 'pack' field from field attributes. - * Valid values are: - * b signed byte - * B unsigned byte - * h signed short - * H unsigned short - * i signed integer - * I unsigned integer - * - * @param data - * @param field + * Valid field packing types are: + * - b signed byte + * - B unsigned byte + * - h signed short + * - H unsigned short + * - i signed integer + * - I unsigned integer + * + * @param {number} 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 */ HIDPacket.prototype.unpack = function(data, field) { let value = 0; @@ -276,8 +310,9 @@ HIDPacket.prototype.unpack = function(data, field) { * Find HID packet group matching name. * Create group if create is true * - * @param name - * @param create + * @param {string} name Name of the group + * @param {boolean} create If true, group will be created + @returns {string} Group Returns group or undefined, when group is not existing and create is set to false */ HIDPacket.prototype.getGroup = function(name, create) { if (this.groups === undefined) { this.groups = {}; } @@ -289,10 +324,18 @@ HIDPacket.prototype.getGroup = function(name, create) { /** * Lookup HID packet field matching given offset and pack type - * Returns undefined if no patching field can be found. * - * @param offset - * @param pack + * @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 + * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 + * @param {object} pack Is one of the field packing types: + * - b signed byte + * - B unsigned byte + * - h signed short + * - H unsigned short + * - i signed integer + * - I unsigned integer + * @returns {packetField} Returns matching field or undefined if no matching field can be found. */ HIDPacket.prototype.getFieldByOffset = function(offset, pack) { if (!(pack in this.packSizes)) { @@ -324,8 +367,9 @@ HIDPacket.prototype.getFieldByOffset = function(offset, pack) { * Return a field by group and name from the packet, * Returns undefined if field could not be found * - * @param group - * @param name + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @returns {packetField} Field */ HIDPacket.prototype.getField = function(group, name) { const field_id = group + "." + name; @@ -358,8 +402,9 @@ HIDPacket.prototype.getField = function(group, name) { /** * Return reference to a bit in a bitvector field * - * @param group - * @param name + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @returns {HIDBitVector} Reference to a bit in a bitvector field */ HIDPacket.prototype.lookupBit = function(group, name) { const field = this.getField(group, name); @@ -379,8 +424,8 @@ HIDPacket.prototype.lookupBit = function(group, name) { /** * Remove a control registered. Normally not needed * - * @param group - * @param name + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) */ HIDPacket.prototype.removeControl = function(group, name) { const control_group = this.getGroup(group); @@ -391,17 +436,27 @@ HIDPacket.prototype.removeControl = function(group, name) { delete control_group[name]; }; +/** + * Callback function to call when, data for specified filed in the packet is updated. + * + * @callback controlCallback + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + */ + /** * Register a numeric value to parse from input packet * - * @param group control group name - * @param name name of the field - * @param offset field offset inside packet (bytes) + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. control group name + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @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 + * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 * @param pack control packing format for unpack(), one of b/B, h/H, i/I - * @param bitmask bitmask size, undefined for byte(s) controls + * @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 isEncoder indicates if this is an encoder which should be wrapped and delta reported - * @param callback callback function for the control + * @param {boolean} isEncoder indicates if this is an encoder which should be wrapped and delta reported + * @param {controlCallback} callback Callback function for the control */ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, isEncoder, callback) { const control_group = this.getGroup(group, true); @@ -469,6 +524,7 @@ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, is field.delta = 0; field.mindelta = 0; } else { + // bitmask is only defined for fields which are not expected to handle all bits in the control field. For fields with bitmasks, you can define same offset and pack multiple times with different bitmask values to get for example all 8 bits of a buttons state byte to different control fields in addControl input packet command. Masking multiple bits should work but has not been as widely tested. if (this.signedPackFormats.indexOf(pack) !== -1) { HIDDebug("ERROR registering bitvector: signed fields not supported"); return; @@ -510,13 +566,14 @@ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, is * It is recommended to define callbacks after packet creation with * setCallback instead of adding it directly here. But you can do it. * - * @param group - * @param name - * @param offset the offset of the byte(s) to use, starting at 1 - * (0 is automatically populated with the reportId) + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @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 + * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 * @param pack control packing format for pack(), one of b/B, h/H, i/I - * @param bitmask - * @param callback + * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. + * @param {controlCallback} callback Callback function for the control */ HIDPacket.prototype.addOutput = function(group, name, offset, pack, bitmask, callback) { const control_group = this.getGroup(group, true); @@ -599,9 +656,9 @@ HIDPacket.prototype.addOutput = function(group, name, offset, pack, bitmask, cal * Register a callback to field or a bit vector bit. * Does not make sense for Output fields but you can do that. * - * @param group - * @param name - * @param callback + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {controlCallback} callback Callback function for the control */ HIDPacket.prototype.setCallback = function(group, name, callback) { const field = this.getField(group, name); @@ -629,12 +686,12 @@ HIDPacket.prototype.setCallback = function(group, name, callback) { }; /** - * Set 'ignored' flag for field to given value (true or false) + * 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 group - * @param name - * @param ignored + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {boolean} ignored 'ignored' flag for field to given value (true or false) */ HIDPacket.prototype.setIgnored = function(group, name, ignored) { const field = this.getField(group, name); @@ -649,9 +706,9 @@ HIDPacket.prototype.setIgnored = function(group, name, ignored) { * Adjust field's minimum delta value. * Input value changes smaller than this are not reported in delta * - * @param group - * @param name - * @param mindelta + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} mindelta Minimum delta value. */ HIDPacket.prototype.setMinDelta = function(group, name, mindelta) { const field = this.getField(group, name); @@ -668,11 +725,10 @@ HIDPacket.prototype.setMinDelta = function(group, name, mindelta) { /** * Parse bitvector field values, returning object with the named bits set. - * Value must be a valid unsigned byte to parse, with enough bits. - * Returns list of modified bits (delta) * - * @param field - * @param value + * @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. + * @returns {HIDBitVector} List of modified bits (delta) */ HIDPacket.prototype.parseBitVector = function(field, value) { const bits = {}; @@ -690,10 +746,10 @@ HIDPacket.prototype.parseBitVector = function(field, value) { /** * Parse input packet fields from data. * Data is expected to be a Packet() received from HID device. - * Returns list of changed fields with new value. * BitVectors are returned as bits you can iterate separately. * - * @param data + * @param {number} data Data received as InputReport from the device A Packet() received from HID device + * @returns List of changed fields with new value. */ HIDPacket.prototype.parse = function(data) { const field_changes = {}; @@ -758,7 +814,7 @@ HIDPacket.prototype.parse = function(data) { * field object values are packed to the HID packet according to the * field type. * - * @param debug + * @param {boolean} debug Enables debug output to console */ HIDPacket.prototype.send = function(debug) { const data = []; @@ -796,31 +852,31 @@ HIDPacket.prototype.send = function(debug) { * HID Controller with packet parser * Global attributes include: * - * initialized by default false, you should set this to true when + * @property {boolean} initialized by default false, you should set this to true when * controller is found and everything is OK - * activeDeck by default undefined, used to map the virtual deck + * @property {string} activeDeck by default undefined, used to map the virtual deck * names 'deck','deck1' and 'deck2' to actual [ChannelX] - * isScratchEnabled set to true, when button 'jog_touch' is active - * buttonStates valid state values for buttons, should contain fields + * @property {boolean} isScratchEnabled set to true, when button 'jog_touch' is active + * @property buttonStates valid state values for buttons, should contain fields * released (default 0) and pressed (default 1) - * LEDColors possible Output colors named, must contain 'off' value - * deckOutputColors Which colors to use for each deck. Default 'on' for first + * @property LEDColors possible Output colors named, must contain 'off' value + * @property deckOutputColors Which colors to use for each deck. Default 'on' for first * four decks. Values are like {1: 'red', 2: 'green' } * and must reference valid OutputColors fields. - * OutputUpdateInterval By default undefined. If set, it's a value for timer + * @property {number} OutputUpdateInterval By default undefined. If set, it's a value for timer * executed every n ms to update Outputs with updateOutputs() - * modifiers Reference to HIDModifierList object - * toggleButtons List of button names you wish to act as 'toggle', i.e. + * @property {HIDModifierList} modifiers Reference to HIDModifierList object + * @property toggleButtons List of button names you wish to act as 'toggle', i.e. * pressing the button and releasing toggles state of the * control and does not set it off again when released. * * Scratch variables (initialized with 'common' defaults, you can override): - * scratchintervalsPerRev Intervals value for scratch_enable - * scratchRPM RPM value for scratch_enable - * scratchAlpha Alpha value for scratch_enable - * scratchBeta Beta value for scratch_enable - * scratchRampOnEnable If 'ramp' is used when enabling scratch - * scratchRampOnDisable If 'ramp' is used when disabling scratch + * @property {number} scratchintervalsPerRev Intervals value for scratch_enable + * @property {number} scratchRPM RPM value for scratch_enable + * @property {number} scratchAlpha Alpha value for scratch_enable + * @property {number} scratchBeta Beta value for scratch_enable + * @property {boolean} scratchRampOnEnable UNUSED If 'ramp' is used when enabling scratch + * @property {boolean} scratchRampOnDisable UNUSED If 'ramp' is used when disabling scratch */ var HIDController = function() { this.initialized = false; @@ -904,7 +960,8 @@ HIDController.prototype.initializePacketData = function() { * Return deck number from deck name. Deck name can't be virtual deck name * in this function call. * - * @param group + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @returns {number} Number of deck */ HIDController.prototype.resolveDeck = function(group) { if (group === undefined) { return undefined; } @@ -917,7 +974,8 @@ HIDController.prototype.resolveDeck = function(group) { /** * Return the group name from given deck number. * - * @param deck + * @param {number} deck Number of deck + * @returns {string} Group name of the deck (e.g. Channel2 for deck number 2) */ HIDController.prototype.resolveDeckGroup = function(deck) { if (deck === undefined) { return undefined; } @@ -928,7 +986,8 @@ HIDController.prototype.resolveDeckGroup = function(deck) { * 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 group + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @returns {string} Channel */ HIDController.prototype.resolveGroup = function(group) { const channel_name = /\[Channel[0-9]+\]/; @@ -953,10 +1012,10 @@ HIDController.prototype.resolveGroup = function(group) { /** * Find Output control matching give group and name - * Returns undefined if output field can't be found. * - * @param m_group - * @param m_name + * @param {string} m_group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} m_name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @returns {HIDBitVector|any} Bitvector or bytewise field - Returns undefined if output field can't be found. */ HIDController.prototype.getOutputField = function(m_group, m_name) { for (const packet_name in this.OutputPackets) { @@ -985,7 +1044,8 @@ HIDController.prototype.getOutputField = function(m_group, m_name) { * Find input packet matching given name. * Returns undefined if input packet name is not registered. * - * @param name + * @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 */ HIDController.prototype.getInputPacket = function(name) { if (!(name in this.InputPackets)) { return undefined; } @@ -996,7 +1056,8 @@ HIDController.prototype.getInputPacket = function(name) { * Find output packet matching given name * Returns undefined if output packet name is not registered. * - * @param name + * @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 */ HIDController.prototype.getOutputPacket = function(name) { if (!(name in this.OutputPackets)) { return undefined; } @@ -1006,8 +1067,8 @@ HIDController.prototype.getOutputPacket = function(name) { /** * Set input packet callback afterwards * - * @param packet - * @param callback + * @param {HIDPacket} packet The input packet + * @param {packetCallback} callback Callback function for the control */ HIDController.prototype.setPacketCallback = function(packet, callback) { const input_packet = this.getInputPacket(packet); @@ -1019,10 +1080,10 @@ HIDController.prototype.setPacketCallback = function(packet, 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 packet - * @param group - * @param name - * @param callback + * @param {HIDPacket} packet The input packet + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {controlCallback} callback Callback function for the control */ HIDController.prototype.setCallback = function(packet, group, name, callback) { const input_packet = this.getInputPacket(packet); @@ -1033,12 +1094,22 @@ HIDController.prototype.setCallback = function(packet, group, name, callback) { input_packet.setCallback(group, name, callback); }; +/** + * In almost every case, a HID controller sends data values with input fields which are not directly suitable for Mixxx control values. To solve this issue, HIDController contains function to scale the input value to suitable range automatically before calling any field processing functions. Scalers can be registered with HIDController.registerScalingFunction(group,name,callback) in HIDController. + * + * @callback scalingCallback + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} value Value to be scaled + * @returns {number} Scaled value + */ + /** * Register scaling function for a control name * This does not check if given control name is valid * - * @param name - * @param callback + * @param {string} name Reference of the scaling function in scalers list of HIDController + * @param {scalingCallback} callback Scaling function */ HIDController.prototype.setScaler = function(name, callback) { if (name in this.scalers) { return; } @@ -1047,10 +1118,10 @@ HIDController.prototype.setScaler = function(name, callback) { /** * Lookup scaling function for control - * Returns undefined if function is not registered. * - * @param name - * @param _callback + * @param {string} name Reference of the scaling function in scalers list of HIDController + * @param _callback Unused + * @returns {scalingCallback} Scaling function. Returns undefined if function is not registered. */ HIDController.prototype.getScaler = function(name, _callback) { if (!(name in this.scalers)) { return undefined; } @@ -1060,8 +1131,8 @@ HIDController.prototype.getScaler = function(name, _callback) { /** * Change type of a previously defined field to modifier and register it * - * @param group - * @param name + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) * @param modifier */ HIDController.prototype.linkModifier = function(group, name, modifier) { @@ -1086,9 +1157,9 @@ HIDController.prototype.linkModifier = function(group, name, modifier) { /** * TODO - implement unlinking of modifiers * - * @param _group - * @param _name - * @param _modifier + * @param {string} _group Unused + * @param {string} _name Unused + * @param _modifier Unused */ HIDController.prototype.unlinkModifier = function(_group, _name, _modifier) { HIDDebug("Unlinking of modifiers not yet implemented"); @@ -1097,11 +1168,11 @@ HIDController.prototype.unlinkModifier = function(_group, _name, _modifier) { /** * Link a previously declared HID control to actual mixxx control * - * @param group - * @param name - * @param m_group - * @param m_name - * @param callback + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} m_group Mapped group + * @param {string} m_name Mapped name + * @param {controlCallback} callback Callback function for the control */ HIDController.prototype.linkControl = function(group, name, m_group, m_name, callback) { let field; @@ -1130,8 +1201,8 @@ HIDController.prototype.linkControl = function(group, name, m_group, m_name, cal /** * TODO - implement unlinking of controls * - * @param _group - * @param _name + * @param {string} _group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} _name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) */ HIDController.prototype.unlinkControl = function(_group, _name) { @@ -1143,7 +1214,7 @@ HIDController.prototype.unlinkControl = function(_group, _name) { * data details. The default control data packet must be named in * variable this.defaultPacket to allow automatic processing. * - * @param packet + * @param {HIDPacket} packet The input packet to register */ HIDController.prototype.registerInputPacket = function(packet) { // Find modifiers and other special cases from packet fields @@ -1174,7 +1245,7 @@ HIDController.prototype.registerInputPacket = function(packet) { * If you need other data structures, patches are welcome, or you can just do it * manually in your script without registering the packet. * - * @param packet + * @param {HIDPacket} packet The output packet to register */ HIDController.prototype.registerOutputPacket = function(packet) { this.OutputPackets[packet.name] = packet; @@ -1195,13 +1266,14 @@ HIDController.prototype.registerOutputPacket = function(packet) { }; /** - * Parse a received input packet fields with "unpack" calls to fields - * Calls packet callback and returns, if packet callback was defined - * Calls processIncomingPacket and processes automated events there. - * If defined, calls processDelta for results after processing automated fields - * - * @param data - * @param length + * 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 processDelta for results after processing automated fields + * + * @param {number[]} 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 */ HIDController.prototype.parsePacket = function(data, length) { let packet; @@ -1212,7 +1284,7 @@ HIDController.prototype.parsePacket = function(data, length) { for (const name in this.InputPackets) { packet = this.InputPackets[name]; - // When the device uses multiple report types with report IDs, hidapi + // When the device uses ReportIDs to enumerate the reports, hidapi // prepends the report ID to the data sent to Mixxx. If the device // only has a single report type, the HIDPacket constructor sets the // reportId as 0. In this case, hidapi only sends the data of the @@ -1250,14 +1322,14 @@ HIDController.prototype.parsePacket = function(data, length) { * Process the modified field values (delta) from input packet fields for * input control packet, if packet name is in this.defaultPacket. * - * Button field processing: + * Button (Boolean value) field processing: * - 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. * - * Control field processing + * 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. @@ -1265,7 +1337,7 @@ HIDController.prototype.parsePacket = function(data, length) { * - Finally tries run matching engine.setValue() function for control * fields in default mixxx groups. Not done if a callback was defined. * - * @param packet + * @param packet Unused * @param delta */ HIDController.prototype.processIncomingPacket = function(packet, delta) { @@ -1274,14 +1346,23 @@ HIDController.prototype.processIncomingPacket = function(packet, delta) { if (this.ignoredControlChanges !== undefined && this.ignoredControlChanges.indexOf(name) !== -1) { continue; } field = delta[name]; - if (field.type === "button") { this.processButton(field); } else if (field.type === "control") { this.processControl(field); } else { HIDDebug("Unknown field " + field.name + " type " + field.type); } + if (field.type === "button") { + // Button/Boolean field + this.processButton(field); + } else if (field.type === "control") { + // Numeric value field + this.processControl(field); + } else { + HIDDebug("Unknown field " + field.name + " type " + field.type); + } } }; /** * Get active group for this field * - * @param field + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + * @returns {string} Group */ HIDController.prototype.getActiveFieldGroup = function(field) { if (field.mapped_group !== undefined) { @@ -1301,7 +1382,8 @@ HIDController.prototype.getActiveFieldGroup = function(field) { /** * Get active control name from field * - * @param field + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + * @returns {string} Name of field */ HIDController.prototype.getActiveFieldControl = function(field) { if (field.mapped_name !== undefined) { return field.mapped_name; } @@ -1311,7 +1393,7 @@ HIDController.prototype.getActiveFieldControl = function(field) { /** * Process given button field, triggering events * - * @param field + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. */ HIDController.prototype.processButton = function(field) { const group = this.getActiveFieldGroup(field); @@ -1369,7 +1451,7 @@ HIDController.prototype.processButton = function(field) { /** * Process given control field, triggering events * - * @param field + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. */ HIDController.prototype.processControl = function(field) { let value; @@ -1422,7 +1504,7 @@ HIDController.prototype.processControl = function(field) { /** * Toggle control state from toggle button * - * @param group + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param control * @param value */ @@ -1435,8 +1517,8 @@ HIDController.prototype.toggle = function(group, control, value) { /** * Toggle play/pause state * - * @param group - * @param field + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. */ HIDController.prototype.togglePlay = function(group, field) { if (field.value === this.buttonStates.released) { return; } @@ -1449,16 +1531,15 @@ HIDController.prototype.togglePlay = function(group, field) { * when scratching should be enabled. * Deck is resolved from group with 'resolveDeck' * - * Enabling scratching (press 'jog_touch' button) + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {boolean} status Enable or Disable scratching: + * - true enables scratching (press 'jog_touch' button) * Sets the internal 'isScratchEnabled' attribute to true, and calls scratchEnable * with the scratch attributes (see class definition) * - * Disabling scratching (release 'jog_touch' button) + * - false disables scratching (release 'jog_touch' button) * Sets the internal 'isScratchEnabled attribute to false, and calls scratchDisable * to end scratching mode - * - * @param group - * @param status */ HIDController.prototype.enableScratch = function(group, status) { const deck = this.resolveDeck(group); @@ -1496,7 +1577,7 @@ HIDController.prototype.enableScratch = function(group, status) { * 'scratchTick' function. Should return -1,0,1 or small ranges of integers * both negative and positive values. * - * @param field + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. */ HIDController.prototype.jog_wheel = function(field) { let scaler = undefined; @@ -1517,6 +1598,11 @@ HIDController.prototype.jog_wheel = function(field) { } }; +/** + * Stops the specified auto repeat timer + * + * @param {number} timer_id Reference of the timer to stop + */ HIDController.prototype.stopAutoRepeatTimer = function(timer_id) { if (this.timers[timer_id]) { engine.stopTimer(this.timers[timer_id]); @@ -1529,10 +1615,10 @@ HIDController.prototype.stopAutoRepeatTimer = function(timer_id) { /** * Toggle field autorepeat on or off * - * @param group - * @param name - * @param callback - * @param interval + * @param {string} group + * @param {string} name + * @param {controlCallback} callback Callback function for the control + * @param {number} interval */ HIDController.prototype.setAutoRepeat = function(group, name, callback, interval) { const packet = this.getInputPacket(this.defaultPacket); @@ -1579,7 +1665,7 @@ HIDController.prototype.autorepeatTimer = function() { /** * Toggle active deck and update virtual output field control mappings * - * @param deck + * @param {number} deck Number of deck */ HIDController.prototype.switchDeck = function(deck) { let packet; @@ -1655,11 +1741,11 @@ HIDController.prototype.switchDeck = function(deck) { /** * Link a virtual HID Output to mixxx control * - * @param group - * @param name - * @param m_group - * @param m_name - * @param callback + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} m_group Mapped group + * @param {string} m_name Name of mapped control + * @param {controlCallback} callback Callback function for the control */ HIDController.prototype.linkOutput = function(group, name, m_group, m_name, callback) { let controlgroup; @@ -1683,9 +1769,9 @@ HIDController.prototype.linkOutput = function(group, name, m_group, m_name, call /** * Unlink a virtual HID Output from mixxx control * - * @param group - * @param name - * @param callback + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {controlCallback} callback Callback function for the control */ HIDController.prototype.unlinkOutput = function(group, name, callback) { const field = this.getOutputField(group, name); @@ -1708,10 +1794,10 @@ HIDController.prototype.unlinkOutput = function(group, name, callback) { /** * Set output state to given value * - * @param group - * @param name - * @param value - * @param send_packet + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} value Value to set as new output state of the control + * @param {boolean} send_packet If true, the packet (an HID OutputReport) is send immediately */ HIDController.prototype.setOutput = function(group, name, value, send_packet) { const field = this.getOutputField(group, name); @@ -1727,8 +1813,8 @@ HIDController.prototype.setOutput = function(group, name, value, send_packet) { /** * Set Output to toggle between two values. Reset with setOutput(name,'off') * - * @param group - * @param name + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) * @param toggle_value */ HIDController.prototype.setOutputToggle = function(group, name, toggle_value) { From 597d1f2c6926e329d2ba4a3500a0e49dc4794467 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Thu, 28 Apr 2022 21:41:43 +0200 Subject: [PATCH 03/60] Use ES6 class syntax Some types documented --- res/controllers/common-hid-packet-parser.js | 3207 +++++++++---------- 1 file changed, 1584 insertions(+), 1623 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 06ec35e8b92..d896342e3f8 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -9,6 +9,27 @@ var HIDDebug = function(message) { print("HID " + message); }; +/** + * @typedef packetField + * @type {object} + * @property {HIDPacket} packet + * @property {string} id Group and control name separated by a dot + * @property {string} group + * @property {string} name + * @property {string} mapped_group + * @property {string} mapped_name + * @property {object} pack Control packing format for unpack(), one of b/B, h/H, i/I + * @property {number} offset + * @property {number} end_offset + * @property {number} bitmask + * @property {boolean} isEncoder + * @property {controlCallback} callback + * @property {boolean} soft_takeover + * @property {boolean} ignored + * @property {boolean} auto_repeat + * @property {number} auto_repeat_interval + */ + /** * HID Bit Vector Class * @@ -16,73 +37,75 @@ var HIDDebug = function(message) { * created by HIDPacket addControl and addOutput and should not be * created manually. */ -var HIDBitVector = function() { - this.size = 0; - 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` - */ -HIDBitVector.prototype.getOffset = function(bitmask) { - for (let i = 0; i < 32; i++) { - if ((1 & bitmask >> i) !== 0) { return i; } - } - return 0; -}; - -/** - * Add a control bitmask to the HIDBitVector - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. - */ -HIDBitVector.prototype.addBitMask = function(group, name, bitmask) { - const bit = {}; - bit.type = "button"; - bit.packet = undefined; - bit.id = group + "." + name; - bit.group = group; - bit.name = name; - bit.mapped_group = undefined; - bit.mapped_name = undefined; - bit.bitmask = bitmask; - bit.bitmask = bitmask; - bit.bit_offset = this.getOffset(bitmask); - bit.callback = undefined; - bit.value = undefined; - bit.auto_repeat = undefined; - bit.auto_repeat_interval = undefined; - this.bits[bit.id] = bit; -}; +class HIDBitVector { + constructor() { + this.size = 0; + 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` + */ + getOffset(bitmask) { + for (let i = 0; i < 32; i++) { + if ((1 & bitmask >> i) !== 0) { return i; } + } + return 0; + } + /** + * Add a control bitmask to the HIDBitVector + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. + */ + addBitMask(group, name, bitmask) { + const bit = {}; + bit.type = "button"; + bit.packet = undefined; + bit.id = group + "." + name; + bit.group = group; + bit.name = name; + bit.mapped_group = undefined; + bit.mapped_name = undefined; + bit.bitmask = bitmask; + bit.bitmask = bitmask; + bit.bit_offset = this.getOffset(bitmask); + bit.callback = undefined; + bit.value = undefined; + bit.auto_repeat = undefined; + bit.auto_repeat_interval = undefined; + this.bits[bit.id] = bit; + } + /** + * Add a Output control bitmask to the HIDBitVector + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. + */ + addOutputMask(group, name, bitmask) { + const bit = {}; + bit.type = "output"; + bit.packet = undefined; + bit.id = group + "." + name; + bit.group = group; + bit.name = name; + bit.mapped_group = undefined; + bit.mapped_name = undefined; + bit.bitmask = bitmask; + bit.bit_offset = this.getOffset(bitmask); + bit.callback = undefined; + bit.value = undefined; + bit.toggle = undefined; + this.bits[bit.id] = bit; + } +} +// Add class HIDBitVector to the Global JavaScript object +this.HIDBitVector = HIDBitVector; -/** - * Add a Output control bitmask to the HIDBitVector - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. - */ -HIDBitVector.prototype.addOutputMask = function(group, name, bitmask) { - const bit = {}; - bit.type = "output"; - bit.packet = undefined; - bit.id = group + "." + name; - bit.group = group; - bit.name = name; - bit.mapped_group = undefined; - bit.mapped_name = undefined; - bit.bitmask = bitmask; - bit.bit_offset = this.getOffset(bitmask); - bit.callback = undefined; - bit.value = undefined; - bit.toggle = undefined; - this.bits[bit.id] = bit; -}; /** * HID Modifiers object @@ -90,78 +113,78 @@ HIDBitVector.prototype.addOutputMask = function(group, name, bitmask) { * Wraps all defined modifiers to one object with uniform API. * Don't call directly, this is available as HIDController.modifiers */ -var HIDModifierList = function() { - this.modifiers = Object(); - this.callbacks = Object(); -}; - -/** - * Add a new modifier to controller. - * - * @param {string} name Name of modifier - */ -HIDModifierList.prototype.add = function(name) { - if (name in this.modifiers) { - HIDDebug("Modifier already defined: " + name); - return; - } - this.modifiers[name] = undefined; -}; - -/** - * Set modifier value - * - * @param {string} name Name of modifier - * @param {number} value Value to be set - */ -HIDModifierList.prototype.set = function(name, value) { - if (!(name in this.modifiers)) { - HIDDebug("Unknown modifier: " + name); - return; - } - this.modifiers[name] = value; - if (name in this.callbacks) { - const callback = this.callbacks[name]; - callback(value); +class HIDModifierList { + constructor() { + this.modifiers = Object(); + this.callbacks = Object(); + } + /** + * Add a new modifier to controller. + * + * @param {string} name Name of modifier + */ + add(name) { + if (name in this.modifiers) { + HIDDebug("Modifier already defined: " + name); + return; + } + this.modifiers[name] = undefined; + } + /** + * Set modifier value + * + * @param {string} name Name of modifier + * @param {number} value Value to be set + */ + set(name, value) { + if (!(name in this.modifiers)) { + HIDDebug("Unknown modifier: " + name); + return; + } + this.modifiers[name] = value; + if (name in this.callbacks) { + const callback = this.callbacks[name]; + callback(value); + } } -}; - -/** - * Get modifier value - * - * @param {string} name Name of modifier - * @returns {number} Value of modifier - */ -HIDModifierList.prototype.get = function(name) { - if (!(name in this.modifiers)) { - HIDDebug("Unknown modifier: " + name); - return false; + /** + * Get modifier value + * + * @param {string} name Name of modifier + * @returns {number} Value of modifier + */ + get(name) { + if (!(name in this.modifiers)) { + HIDDebug("Unknown modifier: " + name); + return false; + } + return this.modifiers[name]; + } + /** + * Callback function to call when, the packet represents an HID InputReport, and new data for this 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 {number[]} changed_data The data received from the device + */ + /** + * Set modifier callback (update function after modifier state changes) + * + * @param {string} name Name of reference in HIDModifierList + * @param {packetCallback} callback Function to be called when value changed + */ + setCallback(name, callback) { + if (!(name in this.modifiers)) { + HIDDebug("Unknown modifier: " + name); + return; + } + this.callbacks[name] = callback; } - return this.modifiers[name]; -}; +} +// Add class HIDModifierList to the Global JavaScript object +this.HIDModifierList = HIDModifierList; -/** - * Callback function to call when, the packet represents an HID InputReport, and new data for this 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 {number[]} changed_data The data received from the device - */ - -/** - * Set modifier callback (update function after modifier state changes) - * - * @param {string} name Name of reference in HIDModifierList - * @param {packetCallback} callback Function to be called when value changed - */ -HIDModifierList.prototype.setCallback = function(name, callback) { - if (!(name in this.modifiers)) { - HIDDebug("Unknown modifier: " + name); - return; - } - this.callbacks[name] = callback; -}; /** * HID Packet object @@ -180,307 +203,345 @@ HIDModifierList.prototype.setCallback = function(name, callback) { * of packet. Do NOT put the report ID in this; use * the reportId parameter instead. */ -var HIDPacket = function(name, reportId, callback, header) { - this.name = name; - this.header = header; - this.callback = callback; - - this.reportId = 0; - if (reportId !== undefined) { - this.reportId = reportId; - } - - this.groups = {}; +class HIDPacket { + constructor(name, reportId, callback, header) { + this.name = name; + this.header = header; + this.callback = callback; - // Size of various 'pack' values in bytes - this.packSizes = {b: 1, B: 1, h: 2, H: 2, i: 4, I: 4}; - this.signedPackFormats = ["b", "h", "i"]; -}; + this.reportId = 0; + if (reportId !== undefined) { + this.reportId = reportId; + } -/** - * @typedef packetField - * @type {object} - * @property {HIDPacket} packet - * @property {string} id Group and control name separated by a dot - * @property {string} group - * @property {string} name - * @property {string} mapped_group - * @property {string} mapped_name - * @property {object} pack Control packing format for unpack(), one of b/B, h/H, i/I - * @property {number} offset - * @property {number} end_offset - * @property {number} bitmask - * @property {boolean} isEncoder - * @property {controlCallback} callback - * @property {boolean} soft_takeover - * @property {boolean} ignored - * @property {boolean} auto_repeat - * @property {number} auto_repeat_interval - */ + this.groups = {}; -/** - * Pack a field value to the packet. - * Can only pack bits and byte values, patches welcome. - * - * @param {number} 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. - */ -HIDPacket.prototype.pack = function(data, field) { - let value; - if (!(field.pack in this.packSizes)) { - HIDDebug("ERROR parsing packed value: invalid pack format " + field.pack); - return; + // Size of various 'pack' values in bytes + this.packSizes = {b: 1, B: 1, h: 2, H: 2, i: 4, I: 4}; + this.signedPackFormats = ["b", "h", "i"]; } - const bytes = this.packSizes[field.pack]; - let signed = false; - if (this.signedPackFormats.indexOf(field.pack) !== -1) { signed = true; } - if (field.type === "bitvector") { - // TODO - fix multi byte bit vector outputs - if (bytes > 1) { - HIDDebug("ERROR: packing multibyte bit vectors not yet supported"); + /** + * Pack a field value to the packet. + * Can only pack bits and byte values, patches welcome. + * + * @param {number} 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. + */ + pack(data, field) { + let value; + if (!(field.pack in this.packSizes)) { + HIDDebug("ERROR parsing packed value: invalid pack format " + field.pack); return; } - for (const bit_id in field.value.bits) { - const bit = field.value.bits[bit_id]; - data[field.offset] = data[field.offset] | bit.value; + const bytes = this.packSizes[field.pack]; + let signed = false; + if (this.signedPackFormats.indexOf(field.pack) !== -1) { signed = true; } + + if (field.type === "bitvector") { + // TODO - fix multi byte bit vector outputs + if (bytes > 1) { + HIDDebug("ERROR: packing multibyte bit vectors not yet supported"); + return; + } + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; + data[field.offset] = data[field.offset] | bit.value; + } + return; } - return; - } - value = (field.value !== undefined) ? field.value : 0; + value = (field.value !== undefined) ? field.value : 0; - if (value < field.min || value > field.max) { - HIDDebug("ERROR " + field.id + " packed value out of range: " + value); - return; - } + if (value < field.min || value > field.max) { + HIDDebug("ERROR " + field.id + " packed value out of range: " + value); + return; + } - for (let byte_index = 0; byte_index < bytes; byte_index++) { - const index = field.offset + byte_index; - if (signed) { - if (value >= 0) { - data[index] = (value >> (byte_index * 8)) & 255; + for (let byte_index = 0; byte_index < bytes; byte_index++) { + const index = field.offset + byte_index; + if (signed) { + if (value >= 0) { + data[index] = (value >> (byte_index * 8)) & 255; + } else { + data[index] = 255 - ((-(value + 1) >> (byte_index * 8)) & 255); + } } else { - data[index] = 255 - ((-(value + 1) >> (byte_index * 8)) & 255); + data[index] = (value >> (byte_index * 8)) & 255; } - } else { - data[index] = (value >> (byte_index * 8)) & 255; } - } - -}; - -/** - * 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 {number} 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 - */ -HIDPacket.prototype.unpack = function(data, field) { - let value = 0; - if (!(field.pack in this.packSizes)) { - HIDDebug("ERROR parsing packed value: invalid pack format " + field.pack); - return; } - const bytes = this.packSizes[field.pack]; - let signed = false; - if (this.signedPackFormats.indexOf(field.pack) !== -1) { signed = true; } + /** + * 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 {number} 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 + */ + unpack(data, field) { + let value = 0; + + if (!(field.pack in this.packSizes)) { + HIDDebug("ERROR parsing packed value: invalid pack format " + field.pack); + return; + } + const bytes = this.packSizes[field.pack]; + let signed = false; + if (this.signedPackFormats.indexOf(field.pack) !== -1) { signed = true; } - for (let field_byte = 0; field_byte < bytes; field_byte++) { - if (data[field.offset + field_byte] === 255 && field_byte === 4) { value += 0; } else { value += data[field.offset + field_byte] * Math.pow(2, (field_byte * 8)); } - } - if (signed) { - const max_value = Math.pow(2, bytes * 8); - const split = max_value / 2 - 1; - if (value > split) { value = value - max_value; } + for (let field_byte = 0; field_byte < bytes; field_byte++) { + if (data[field.offset + field_byte] === 255 && field_byte === 4) { value += 0; } else { value += data[field.offset + field_byte] * Math.pow(2, (field_byte * 8)); } + } + if (signed) { + const max_value = Math.pow(2, bytes * 8); + const split = max_value / 2 - 1; + if (value > split) { value = value - max_value; } + } + return value; + } + /** + * Find HID packet group matching name. + * Create group if create is true + * + * @param {string} name Name of the group + * @param {boolean} create If true, group will be created + @returns {string} Group Returns group or undefined, when group is not existing and create is set to false + */ + getGroup(name, create) { + if (this.groups === undefined) { this.groups = {}; } + if (name in this.groups) { return this.groups[name]; } + if (!create) { return undefined; } + this.groups[name] = {}; + return this.groups[name]; + } + /** + * 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 + * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 + * @param {object} pack Is one of the field packing types: + * - b signed byte + * - B unsigned byte + * - h signed short + * - H unsigned short + * - i signed integer + * - I unsigned integer + * @returns {packetField} Returns matching field or undefined if no matching field can be found. + */ + getFieldByOffset(offset, pack) { + if (!(pack in this.packSizes)) { + HIDDebug("Unknown pack string " + pack); + return undefined; + } + const end_offset = offset + this.packSizes[pack]; + let group; + let field; + for (const group_name in this.groups) { + group = this.groups[group_name]; + for (const field_id in group) { + field = group[field_id]; + // Same field offset + if (field.offset === offset) { return field; } + // 7-8 8-9 + // Offset for smaller packet inside multibyte field + if (field.offset < offset && field.end_offset >= end_offset) { return field; } + // Packet offset starts inside field, may overflow + if (field.offset < offset && field.end_offset > offset) { return field; } + // Packet start before field, ends or overflows field + if (field.offset > offset && field.offset < end_offset) { return field; } + } + } + return undefined; } - return value; -}; - -/** - * Find HID packet group matching name. - * Create group if create is true - * - * @param {string} name Name of the group - * @param {boolean} create If true, group will be created - @returns {string} Group Returns group or undefined, when group is not existing and create is set to false - */ -HIDPacket.prototype.getGroup = function(name, create) { - if (this.groups === undefined) { this.groups = {}; } - if (name in this.groups) { return this.groups[name]; } - if (!create) { return undefined; } - this.groups[name] = {}; - return this.groups[name]; -}; + /** + * Return a field by group and name from the packet, + * Returns undefined if field could not be found + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @returns {packetField} Field + */ + getField(group, name) { + const field_id = group + "." + name; + if (!(group in this.groups)) { + HIDDebug("PACKET " + this.name + " group not found " + group); + return undefined; + } -/** - * 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 - * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 - * @param {object} pack Is one of the field packing types: - * - b signed byte - * - B unsigned byte - * - h signed short - * - H unsigned short - * - i signed integer - * - I unsigned integer - * @returns {packetField} Returns matching field or undefined if no matching field can be found. - */ -HIDPacket.prototype.getFieldByOffset = function(offset, pack) { - if (!(pack in this.packSizes)) { - HIDDebug("Unknown pack string " + pack); + let control_group = this.groups[group]; + if (field_id in control_group) { return control_group[field_id]; } + + // Lookup for bit fields in bitvector matching field name + for (const group_name in this.groups) { + control_group = this.groups[group_name]; + for (const field_name in control_group) { + const field = control_group[field_name]; + if (field === undefined || field.type !== "bitvector") { continue; } + for (const bit_name in field.value.bits) { + const bit = field.value.bits[bit_name]; + if (bit.id === field_id) { + return field; + } + } + } + } + // Field not found return undefined; } - const end_offset = offset + this.packSizes[pack]; - let group; - let field; - for (const group_name in this.groups) { - group = this.groups[group_name]; - for (const field_id in group) { - field = group[field_id]; - // Same field offset - if (field.offset === offset) { return field; } - // 7-8 8-9 - // Offset for smaller packet inside multibyte field - if (field.offset < offset && field.end_offset >= end_offset) { return field; } - // Packet offset starts inside field, may overflow - if (field.offset < offset && field.end_offset > offset) { return field; } - // Packet start before field, ends or overflows field - if (field.offset > offset && field.offset < end_offset) { return field; } + /** + * Return reference to a bit in a bitvector field + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @returns {HIDBitVector} Reference to a bit in a bitvector field + */ + lookupBit(group, name) { + const field = this.getField(group, name); + if (field === undefined) { + HIDDebug("Bitvector match not found: " + group + "." + name); + return undefined; } - } - return undefined; -}; - -/** - * Return a field by group and name from the packet, - * Returns undefined if field could not be found - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @returns {packetField} Field - */ -HIDPacket.prototype.getField = function(group, name) { - const field_id = group + "." + name; - if (!(group in this.groups)) { - HIDDebug("PACKET " + this.name + " group not found " + group); + const bit_id = group + "." + name; + for (const bit_name in field.value.bits) { + const bit = field.value.bits[bit_name]; + if (bit.id === bit_id) { return bit; } + } + HIDDebug("BUG: bit not found after successful field lookup"); return undefined; } + /** + * Remove a control registered. Normally not needed + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + */ + removeControl(group, name) { + const control_group = this.getGroup(group); + if (!(name in control_group)) { + HIDDebug("Field not in control group " + group + ": " + name); + return; + } + delete control_group[name]; + } + /** + * Callback function to call when, data for specified filed in the packet is updated. + * + * @callback controlCallback + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + */ + /** + * Register a numeric value to parse from input packet + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. control group name + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @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 + * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 + * @param pack control packing format for unpack(), one of b/B, h/H, i/I + * @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 reported + * @param {controlCallback} callback Callback function for the control + */ + addControl(group, name, offset, pack, bitmask, isEncoder, callback) { + const control_group = this.getGroup(group, true); + let bitvector = undefined; + if (control_group === undefined) { + HIDDebug("ERROR creating HID packet group " + group); + return; + } + if (!(pack in this.packSizes)) { + HIDDebug("Unknown pack value " + pack); + return; + } - let control_group = this.groups[group]; - if (field_id in control_group) { return control_group[field_id]; } - - // Lookup for bit fields in bitvector matching field name - for (const group_name in this.groups) { - control_group = this.groups[group_name]; - for (const field_name in control_group) { - const field = control_group[field_name]; - if (field === undefined || field.type !== "bitvector") { continue; } - for (const bit_name in field.value.bits) { - const bit = field.value.bits[bit_name]; - if (bit.id === field_id) { - return field; + let field = this.getFieldByOffset(offset, pack); + if (field !== undefined) { + if (bitmask === undefined) { + HIDDebug("ERROR registering offset " + offset + " pack " + pack); + HIDDebug( + "ERROR trying to overwrite non-bitmask control " + group + " " + name + ); + return; + } + bitvector = field.value; + bitvector.addBitMask(group, name, bitmask); + if (callback !== undefined) { + if (typeof callback !== "function") { + HIDDebug("ERROR callback provided for " + group + "." + name + " is not a function."); + return; } + this.setCallback(group, name, callback); } + return; } - } - // Field not found - return undefined; -}; - -/** - * Return reference to a bit in a bitvector field - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @returns {HIDBitVector} Reference to a bit in a bitvector field - */ -HIDPacket.prototype.lookupBit = function(group, name) { - const field = this.getField(group, name); - if (field === undefined) { - HIDDebug("Bitvector match not found: " + group + "." + name); - return undefined; - } - const bit_id = group + "." + name; - for (const bit_name in field.value.bits) { - const bit = field.value.bits[bit_name]; - if (bit.id === bit_id) { return bit; } - } - HIDDebug("BUG: bit not found after successful field lookup"); - return undefined; -}; -/** - * Remove a control registered. Normally not needed - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - */ -HIDPacket.prototype.removeControl = function(group, name) { - const control_group = this.getGroup(group); - if (!(name in control_group)) { - HIDDebug("Field not in control group " + group + ": " + name); - return; - } - delete control_group[name]; -}; + field = {}; + field.packet = undefined; + field.id = group + "." + name; + field.group = group; + field.name = name; + field.mapped_group = undefined; + field.mapped_name = undefined; + field.pack = pack; + field.offset = offset; + field.end_offset = offset + this.packSizes[field.pack]; + field.bitmask = bitmask; + field.isEncoder = isEncoder; + field.callback = undefined; + field.soft_takeover = false; + field.ignored = false; + field.auto_repeat = undefined; + field.auto_repeat_interval = undefined; + + const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); + if (this.signedPackFormats.indexOf(pack) !== -1) { + field.min = 0 - (packet_max_value / 2) + 1; + field.max = (packet_max_value / 2) - 1; + } else { + field.min = 0; + field.max = packet_max_value - 1; + } -/** - * Callback function to call when, data for specified filed in the packet is updated. - * - * @callback controlCallback - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. - */ + if (bitmask === undefined || bitmask === packet_max_value) { + field.type = "control"; + field.value = undefined; + field.delta = 0; + field.mindelta = 0; + } else { + // bitmask is only defined for fields which are not expected to handle all bits in the control field. For fields with bitmasks, you can define same offset and pack multiple times with different bitmask values to get for example all 8 bits of a buttons state byte to different control fields in addControl input packet command. Masking multiple bits should work but has not been as widely tested. + if (this.signedPackFormats.indexOf(pack) !== -1) { + HIDDebug("ERROR registering bitvector: signed fields not supported"); + return; + } + // Create a new bitvector field and add the bit to that + // TODO - accept controls with bitmask < packet_max_value + const field_name = "bitvector_" + offset; + field.type = "bitvector"; + field.name = field_name; + field.id = group + "." + field_name; + bitvector = new HIDBitVector(field.max); + bitvector.size = field.max; + bitvector.addBitMask(group, name, bitmask); + field.value = bitvector; + field.delta = undefined; + field.soft_takeover = undefined; + field.mindelta = undefined; + } -/** - * Register a numeric value to parse from input packet - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. control group name - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @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 - * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 - * @param pack control packing format for unpack(), one of b/B, h/H, i/I - * @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 reported - * @param {controlCallback} callback Callback function for the control - */ -HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, isEncoder, callback) { - const control_group = this.getGroup(group, true); - let bitvector = undefined; - if (control_group === undefined) { - HIDDebug("ERROR creating HID packet group " + group); - return; - } - if (!(pack in this.packSizes)) { - HIDDebug("Unknown pack value " + pack); - return; - } + // Add the new field to the packet + control_group[field.id] = field; - let field = this.getFieldByOffset(offset, pack); - if (field !== undefined) { - if (bitmask === undefined) { - HIDDebug("ERROR registering offset " + offset + " pack " + pack); - HIDDebug( - "ERROR trying to overwrite non-bitmask control " + group + " " + name - ); - return; - } - bitvector = field.value; - bitvector.addBitMask(group, name, bitmask); if (callback !== undefined) { if (typeof callback !== "function") { HIDDebug("ERROR callback provided for " + group + "." + name + " is not a function."); @@ -488,363 +549,294 @@ HIDPacket.prototype.addControl = function(group, name, offset, pack, bitmask, is } this.setCallback(group, name, callback); } - return; - } - - field = {}; - field.packet = undefined; - field.id = group + "." + name; - field.group = group; - field.name = name; - field.mapped_group = undefined; - field.mapped_name = undefined; - field.pack = pack; - field.offset = offset; - field.end_offset = offset + this.packSizes[field.pack]; - field.bitmask = bitmask; - field.isEncoder = isEncoder; - field.callback = undefined; - field.soft_takeover = false; - field.ignored = false; - field.auto_repeat = undefined; - field.auto_repeat_interval = undefined; - - const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); - if (this.signedPackFormats.indexOf(pack) !== -1) { - field.min = 0 - (packet_max_value / 2) + 1; - field.max = (packet_max_value / 2) - 1; - } else { - field.min = 0; - field.max = packet_max_value - 1; } - - if (bitmask === undefined || bitmask === packet_max_value) { - field.type = "control"; - field.value = undefined; - field.delta = 0; - field.mindelta = 0; - } else { - // bitmask is only defined for fields which are not expected to handle all bits in the control field. For fields with bitmasks, you can define same offset and pack multiple times with different bitmask values to get for example all 8 bits of a buttons state byte to different control fields in addControl input packet command. Masking multiple bits should work but has not been as widely tested. - if (this.signedPackFormats.indexOf(pack) !== -1) { - HIDDebug("ERROR registering bitvector: signed fields not supported"); + /** + * 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 control 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @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 + * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 + * @param pack control packing format for pack(), one of b/B, h/H, i/I + * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. + * @param {controlCallback} callback Callback function for the control + */ + addOutput(group, name, offset, pack, bitmask, callback) { + const control_group = this.getGroup(group, true); + let field; + let bitvector = undefined; + const field_id = group + "." + name; + + if (control_group === undefined) { return; } - // Create a new bitvector field and add the bit to that - // TODO - accept controls with bitmask < packet_max_value - const field_name = "bitvector_" + offset; - field.type = "bitvector"; - field.name = field_name; - field.id = group + "." + field_name; - bitvector = new HIDBitVector(field.max); - bitvector.size = field.max; - bitvector.addBitMask(group, name, bitmask); - field.value = bitvector; - field.delta = undefined; - field.soft_takeover = undefined; - field.mindelta = undefined; - } - - // Add the new field to the packet - control_group[field.id] = field; - - if (callback !== undefined) { - if (typeof callback !== "function") { - HIDDebug("ERROR callback provided for " + group + "." + name + " is not a function."); + if (!(pack in this.packSizes)) { + HIDDebug("ERROR: unknown Output control pack value " + pack); return; } - this.setCallback(group, name, callback); - } -}; -/** - * 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 control 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @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 - * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 - * @param pack control packing format for pack(), one of b/B, h/H, i/I - * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. - * @param {controlCallback} callback Callback function for the control - */ -HIDPacket.prototype.addOutput = function(group, name, offset, pack, bitmask, callback) { - const control_group = this.getGroup(group, true); - let field; - let bitvector = undefined; - const field_id = group + "." + name; + // Adjust offset by 1 because the reportId was previously considered part of the payload + // but isn't anymore and we can't be bothered to adjust every single script manually + offset -= 1; + + // Check if we are adding a Output bit to existing bitvector + field = this.getFieldByOffset(offset, pack); + if (field !== undefined) { + if (bitmask === undefined) { + HIDDebug( + "ERROR: overwrite non-bitmask control " + group + "." + name + ); + return; + } + bitvector = field.value; + bitvector.addOutputMask(group, name, bitmask); + return; + } - if (control_group === undefined) { - return; - } - if (!(pack in this.packSizes)) { - HIDDebug("ERROR: unknown Output control pack value " + pack); - return; - } + field = {}; + field.id = field_id; + field.group = group; + field.name = name; + field.mapped_group = undefined; + field.mapped_name = undefined; + field.pack = pack; + field.offset = offset; + field.end_offset = offset + this.packSizes[field.pack]; + field.bitmask = bitmask; + field.callback = callback; + field.toggle = undefined; - // Adjust offset by 1 because the reportId was previously considered part of the payload - // but isn't anymore and we can't be bothered to adjust every single script manually - offset -= 1; + const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); + if (this.signedPackFormats.indexOf(pack) !== -1) { + field.min = 0 - (packet_max_value / 2) + 1; + field.max = (packet_max_value / 2) - 1; + } else { + field.min = 0; + field.max = packet_max_value - 1; + } + if (bitmask === undefined || bitmask === packet_max_value) { + field.type = "output"; + field.value = undefined; + field.delta = undefined; + field.mindelta = undefined; + } else { + // Create new Output bitvector control field, add bit to it + // rewrite name to use bitvector instead + const field_name = "bitvector_" + offset; + field.type = "bitvector"; + field.id = group + "." + field_name; + field.name = field_name; + bitvector = new HIDBitVector(); + bitvector.size = field.max; + bitvector.addOutputMask(group, name, bitmask); + field.value = bitvector; + field.delta = undefined; + field.mindelta = undefined; + } - // Check if we are adding a Output bit to existing bitvector - field = this.getFieldByOffset(offset, pack); - if (field !== undefined) { - if (bitmask === undefined) { - HIDDebug( - "ERROR: overwrite non-bitmask control " + group + "." + name + // Add Output to HID packet + control_group[field.id] = field; + } + /** + * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {controlCallback} callback Callback function for the control + */ + setCallback(group, name, callback) { + const field = this.getField(group, name); + const field_id = group + "." + name; + if (callback === undefined) { + HIDDebug("Callback to add was undefined for " + field_id); + return; + } + if (field === undefined) { + HIDDebug("setCallback: field for " + field_id + " not found" ); return; } - bitvector = field.value; - bitvector.addOutputMask(group, name, bitmask); - return; - } - - field = {}; - field.id = field_id; - field.group = group; - field.name = name; - field.mapped_group = undefined; - field.mapped_name = undefined; - field.pack = pack; - field.offset = offset; - field.end_offset = offset + this.packSizes[field.pack]; - field.bitmask = bitmask; - field.callback = callback; - field.toggle = undefined; - - const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); - if (this.signedPackFormats.indexOf(pack) !== -1) { - field.min = 0 - (packet_max_value / 2) + 1; - field.max = (packet_max_value / 2) - 1; - } else { - field.min = 0; - field.max = packet_max_value - 1; - } - if (bitmask === undefined || bitmask === packet_max_value) { - field.type = "output"; - field.value = undefined; - field.delta = undefined; - field.mindelta = undefined; - } else { - // Create new Output bitvector control field, add bit to it - // rewrite name to use bitvector instead - const field_name = "bitvector_" + offset; - field.type = "bitvector"; - field.id = group + "." + field_name; - field.name = field_name; - bitvector = new HIDBitVector(); - bitvector.size = field.max; - bitvector.addOutputMask(group, name, bitmask); - field.value = bitvector; - field.delta = undefined; - field.mindelta = undefined; - } - - // Add Output to HID packet - control_group[field.id] = field; -}; - -/** - * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {controlCallback} callback Callback function for the control - */ -HIDPacket.prototype.setCallback = function(group, name, callback) { - const field = this.getField(group, name); - const field_id = group + "." + name; - if (callback === undefined) { - HIDDebug("Callback to add was undefined for " + field_id); - return; - } - if (field === undefined) { - HIDDebug("setCallback: field for " + field_id + " not found" - ); - return; + if (field.type === "bitvector") { + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; + if (bit_id !== field_id) { continue; } + bit.callback = callback; + return; + } + HIDDebug("ERROR: BIT NOT FOUND " + field_id); + } else { + field.callback = callback; + } } - if (field.type === "bitvector") { - for (const bit_id in field.value.bits) { - const bit = field.value.bits[bit_id]; - if (bit_id !== field_id) { continue; } - bit.callback = callback; + /** + * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {boolean} ignored 'ignored' flag for field to given value (true or false) + */ + setIgnored(group, name, ignored) { + const field = this.getField(group, name); + if (field === undefined) { + HIDDebug("ERROR setting ignored flag for " + group + " " + name); return; } - HIDDebug("ERROR: BIT NOT FOUND " + field_id); - } else { - field.callback = callback; - } -}; - -/** - * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {boolean} ignored 'ignored' flag for field to given value (true or false) - */ -HIDPacket.prototype.setIgnored = function(group, name, ignored) { - const field = this.getField(group, name); - if (field === undefined) { - HIDDebug("ERROR setting ignored flag for " + group + " " + name); - return; - } - field.ignored = ignored; -}; - -/** - * Adjust field's minimum delta value. - * Input value changes smaller than this are not reported in delta - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {number} mindelta Minimum delta value. - */ -HIDPacket.prototype.setMinDelta = function(group, name, mindelta) { - const field = this.getField(group, name); - if (field === undefined) { - HIDDebug("ERROR adjusting mindelta for " + group + " " + name); - return; - } - if (field.type === "bitvector") { - HIDDebug("ERROR setting mindelta for bitvector packet does not make sense"); - return; - } - field.mindelta = mindelta; -}; - -/** - * 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. - * @returns {HIDBitVector} List of modified bits (delta) - */ -HIDPacket.prototype.parseBitVector = function(field, value) { - const bits = {}; - let bit; - let new_value; - for (const bit_id in field.value.bits) { - bit = field.value.bits[bit_id]; - new_value = (bit.bitmask & value) >> bit.bit_offset; - if (bit.value !== undefined && bit.value !== new_value) { bits[bit_id] = bit; } - bit.value = new_value; - } - return bits; -}; - -/** - * 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 {number} data Data received as InputReport from the device A Packet() received from HID device - * @returns List of changed fields with new value. - */ -HIDPacket.prototype.parse = function(data) { - const field_changes = {}; - let group; - let group_name; - let field; - let field_id; - - for (group_name in this.groups) { - group = this.groups[group_name]; - for (field_id in group) { - field = group[field_id]; - if (field === undefined) { continue; } - - const value = this.unpack(data, field); - if (value === undefined) { - HIDDebug("Error parsing packet field value for " + field_id); - return; - } - - if (field.type === "bitvector") { - // Bitvector deltas are checked in parseBitVector - const changed_bits = this.parseBitVector(field, value); - for (const bit_name in changed_bits) { field_changes[bit_name] = changed_bits[bit_name]; } - - } else if (field.type === "control") { - if (field.value === value && field.mindelta !== undefined) { continue; } - if (field.ignored || field.value === undefined) { - field.value = value; - continue; + field.ignored = ignored; + } + /** + * Adjust field's minimum delta value. + * Input value changes smaller than this are not reported in delta + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} mindelta Minimum delta value. + */ + setMinDelta(group, name, mindelta) { + const field = this.getField(group, name); + if (field === undefined) { + HIDDebug("ERROR adjusting mindelta for " + group + " " + name); + return; + } + if (field.type === "bitvector") { + HIDDebug("ERROR setting mindelta for bitvector packet does not make sense"); + return; + } + field.mindelta = mindelta; + } + /** + * 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. + * @returns {HIDBitVector} List of modified bits (delta) + */ + parseBitVector(field, value) { + const bits = {}; + let bit; + let new_value; + for (const bit_id in field.value.bits) { + bit = field.value.bits[bit_id]; + new_value = (bit.bitmask & value) >> bit.bit_offset; + if (bit.value !== undefined && bit.value !== new_value) { bits[bit_id] = bit; } + bit.value = new_value; + } + return bits; + } + /** + * 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 {number} data Data received as InputReport from the device + * @returns List of changed fields with new value. + */ + parse(data) { + const field_changes = {}; + let group; + let group_name; + let field; + let field_id; + + for (group_name in this.groups) { + group = this.groups[group_name]; + for (field_id in group) { + field = group[field_id]; + if (field === undefined) { continue; } + + const value = this.unpack(data, field); + if (value === undefined) { + HIDDebug("Error parsing packet field value for " + field_id); + return; } - var change; - if (field.isEncoder) { - if (field.value === field.max && value === field.min) { - change = 1; - field.delta = 1; - } else if (value === field.max && field.value === field.min) { - change = 1; - field.delta = -1; + + if (field.type === "bitvector") { + // Bitvector deltas are checked in parseBitVector + const changed_bits = this.parseBitVector(field, value); + for (const bit_name in changed_bits) { field_changes[bit_name] = changed_bits[bit_name]; } + + } else if (field.type === "control") { + if (field.value === value && field.mindelta !== undefined) { continue; } + if (field.ignored || field.value === undefined) { + field.value = value; + continue; + } + var change; + if (field.isEncoder) { + if (field.value === field.max && value === field.min) { + change = 1; + field.delta = 1; + } else if (value === field.max && field.value === field.min) { + change = 1; + field.delta = -1; + } else { + change = 1; + field.delta = value - field.value; + } + field.value = value; } else { - change = 1; + change = Math.abs(value - field.value); field.delta = value - field.value; } - field.value = value; - } else { - change = Math.abs(value - field.value); - field.delta = value - field.value; - } - if (field.mindelta === undefined || change > field.mindelta) { - field_changes[field.id] = field; - field.value = value; + if (field.mindelta === undefined || change > field.mindelta) { + field_changes[field.id] = field; + field.value = value; + } } } } - } - return field_changes; -}; - -/** - * Send this HID packet to device. - * 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 Enables debug output to console - */ -HIDPacket.prototype.send = function(debug) { - const data = []; - - if (this.header !== undefined) { - for (let header_byte = 0; header_byte < this.header.length; header_byte++) { - data[header_byte] = this.header[header_byte]; + return field_changes; + } + /** + * Send this HID packet to device. + * 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 Enables debug output to console + */ + send(debug) { + const data = []; + + if (this.header !== undefined) { + for (let header_byte = 0; header_byte < this.header.length; header_byte++) { + data[header_byte] = this.header[header_byte]; + } } - } - for (const group_name in this.groups) { - const group = this.groups[group_name]; - for (const field_name in group) { - this.pack(data, group[field_name]); + for (const group_name in this.groups) { + const group = this.groups[group_name]; + for (const field_name in group) { + this.pack(data, group[field_name]); + } } - } - if (debug) { - let packet_string = ""; - for (const d in data) { - if (data[d] < 0x10) { - // Add padding for bytes smaller than 10 - packet_string += "0"; + if (debug) { + let packet_string = ""; + for (const d in data) { + if (data[d] < 0x10) { + // Add padding for bytes smaller than 10 + packet_string += "0"; + } + packet_string += data[d].toString(16) + " "; } - packet_string += data[d].toString(16) + " "; + HIDDebug("Sending packet with Report ID " + this.reportId + ": " + packet_string); } - HIDDebug("Sending packet with Report ID " + this.reportId + ": " + packet_string); + controller.send(data, data.length, this.reportId); } - controller.send(data, data.length, this.reportId); -}; +} +// Add class HIDPacket to the Global JavaScript object +this.HIDPacket = HIDPacket; + /** * HID Controller Class @@ -878,952 +870,921 @@ HIDPacket.prototype.send = function(debug) { * @property {boolean} scratchRampOnEnable UNUSED If 'ramp' is used when enabling scratch * @property {boolean} scratchRampOnDisable UNUSED If 'ramp' is used when disabling scratch */ -var HIDController = function() { - this.initialized = false; - this.activeDeck = undefined; - - this.InputPackets = {}; - this.OutputPackets = {}; - // Default input control packet name: can be modified for controllers - // which can swap modes (wiimote for example) - this.defaultPacket = "control"; - - // Callback functions called by deck switching. Undefined by default - this.disconnectDeck = undefined; - this.connectDeck = undefined; - - // Scratch parameter defaults for this.scratchEnable function - // override for custom control - this.isScratchEnabled = false; - this.scratchintervalsPerRev = 128; - this.scratchRPM = 33 + 1 / 3; - this.scratchAlpha = 1.0 / 8; - this.scratchBeta = this.scratchAlpha / 32; - this.scratchRampOnEnable = false; - this.scratchRampOnDisable = false; - - // Button states available - this.buttonStates = {released: 0, pressed: 1}; - // Output color values to send - this.LEDColors = {off: 0x0, on: 0x7f}; - // Toggle buttons - this.toggleButtons = ["play", "pfl", "keylock", "quantize", "reverse", "slip_enabled", - "group_[Channel1]_enable", "group_[Channel2]_enable", - "group_[Channel3]_enable", "group_[Channel4]_enable"]; - - // Override to set specific colors for multicolor button Output per deck - this.deckOutputColors = {1: "on", 2: "on", 3: "on", 4: "on"}; - // Mapping of automatic deck switching with deckSwitch function - this.virtualDecks = ["deck", "deck1", "deck2", "deck3", "deck4"]; - this.deckSwitchMap = {1: 2, 2: 1, 3: 4, 4: 3, undefined: 1}; - - // Standard target groups available in mixxx. This is used by - // HID packet parser to recognize group parameters we should - // try sending to mixxx. - this.valid_groups = [ - "[Channel1]", "[Channel2]", "[Channel3]", "[Channel4]", - "[Sampler1]", "[Sampler2]", "[Sampler3]", "[Sampler4]", - "[Sampler5]", "[Sampler6]", "[Sampler7]", "[Sampler8]", - "[Master]", "[PreviewDeck1]", "[Effects]", "[Playlist]", "[Flanger]", - "[Microphone]", "[EffectRack1_EffectUnit1]", "[EffectRack1_EffectUnit2]", - "[EffectRack1_EffectUnit3]", "[EffectRack1_EffectUnit4]", - "[InternalClock]"]; - - // Set to value in ms to update Outputs periodically - this.OutputUpdateInterval = undefined; - - this.modifiers = new HIDModifierList(); - this.scalers = {}; - this.timers = {}; - - this.auto_repeat_interval = 100; -}; - -/** Function to close the controller object cleanly */ -HIDController.prototype.close = function() { - for (const name in this.timers) { - const timer = this.timers[name]; - if (timer === undefined) { continue; } - engine.stopTimer(timer); - this.timers[name] = undefined; - } -}; - -/** - * Initialize our packet data and callbacks. This does not seem to - * work when executed from here, but we keep a stub just in case. - */ -HIDController.prototype.initializePacketData = function() { -}; +class HIDController { + constructor() { + this.initialized = false; + this.activeDeck = undefined; -/** - * Return deck number from deck name. Deck name can't be virtual deck name - * in this function call. - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @returns {number} Number of deck - */ -HIDController.prototype.resolveDeck = function(group) { - if (group === undefined) { return undefined; } - const result = group.match(/\[Channel[0-9]+\]/); - if (!result) { return undefined; } - const str = group.replace(/\[Channel/, ""); - return str.substring(0, str.length - 1); -}; + this.InputPackets = {}; + this.OutputPackets = {}; + // Default input control packet name: can be modified for controllers + // which can swap modes (wiimote for example) + this.defaultPacket = "control"; -/** - * 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) - */ -HIDController.prototype.resolveDeckGroup = function(deck) { - if (deck === undefined) { return undefined; } - return "[Channel" + deck + "]"; -}; + // Callback functions called by deck switching. Undefined by default + this.disconnectDeck = undefined; + this.connectDeck = undefined; -/** - * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @returns {string} Channel - */ -HIDController.prototype.resolveGroup = function(group) { - const channel_name = /\[Channel[0-9]+\]/; - if (group !== undefined && group.match(channel_name)) { return group; } - if (this.valid_groups.indexOf(group) !== -1) { - return group; - } - if (group === "deck" || group === undefined) { - if (this.activeDeck === undefined) { return undefined; } - return "[Channel" + this.activeDeck + "]"; + // Scratch parameter defaults for this.scratchEnable function + // override for custom control + this.isScratchEnabled = false; + this.scratchintervalsPerRev = 128; + this.scratchRPM = 33 + 1 / 3; + this.scratchAlpha = 1.0 / 8; + this.scratchBeta = this.scratchAlpha / 32; + this.scratchRampOnEnable = false; + this.scratchRampOnDisable = false; + + // Button states available + this.buttonStates = {released: 0, pressed: 1}; + // Output color values to send + this.LEDColors = {off: 0x0, on: 0x7f}; + // Toggle buttons + this.toggleButtons = ["play", "pfl", "keylock", "quantize", "reverse", "slip_enabled", + "group_[Channel1]_enable", "group_[Channel2]_enable", + "group_[Channel3]_enable", "group_[Channel4]_enable"]; + + // Override to set specific colors for multicolor button Output per deck + this.deckOutputColors = {1: "on", 2: "on", 3: "on", 4: "on"}; + // Mapping of automatic deck switching with deckSwitch function + this.virtualDecks = ["deck", "deck1", "deck2", "deck3", "deck4"]; + this.deckSwitchMap = {1: 2, 2: 1, 3: 4, 4: 3, undefined: 1}; + + // Standard target groups available in mixxx. This is used by + // HID packet parser to recognize group parameters we should + // try sending to mixxx. + this.valid_groups = [ + "[Channel1]", "[Channel2]", "[Channel3]", "[Channel4]", + "[Sampler1]", "[Sampler2]", "[Sampler3]", "[Sampler4]", + "[Sampler5]", "[Sampler6]", "[Sampler7]", "[Sampler8]", + "[Master]", "[PreviewDeck1]", "[Effects]", "[Playlist]", "[Flanger]", + "[Microphone]", "[EffectRack1_EffectUnit1]", "[EffectRack1_EffectUnit2]", + "[EffectRack1_EffectUnit3]", "[EffectRack1_EffectUnit4]", + "[InternalClock]" + ]; + + // Set to value in ms to update Outputs periodically + this.OutputUpdateInterval = undefined; + + this.modifiers = new HIDModifierList(); + this.scalers = {}; + this.timers = {}; + + this.auto_repeat_interval = 100; + } + /** Function to close the controller object cleanly */ + close() { + for (const name in this.timers) { + const timer = this.timers[name]; + if (timer === undefined) { continue; } + engine.stopTimer(timer); + this.timers[name] = undefined; + } } - if (this.activeDeck === 1 || this.activeDeck === 2) { - if (group === "deck1") { return "[Channel1]"; } - if (group === "deck2") { return "[Channel2]"; } + /** + * Initialize our packet data and callbacks. This does not seem to + * work when executed from here, but we keep a stub just in case. + */ + initializePacketData() { + } + /** + * Return deck number from deck name. Deck name can't be virtual deck name + * in this function call. + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @returns {number} Number of deck + */ + resolveDeck(group) { + if (group === undefined) { return undefined; } + const result = group.match(/\[Channel[0-9]+\]/); + if (!result) { return undefined; } + const str = group.replace(/\[Channel/, ""); + return str.substring(0, str.length - 1); + } + /** + * 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) + */ + resolveDeckGroup(deck) { + if (deck === undefined) { return undefined; } + return "[Channel" + deck + "]"; + } + /** + * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @returns {string} Channel + */ + resolveGroup(group) { + const channel_name = /\[Channel[0-9]+\]/; + if (group !== undefined && group.match(channel_name)) { return group; } + if (this.valid_groups.indexOf(group) !== -1) { + return group; + } + if (group === "deck" || group === undefined) { + if (this.activeDeck === undefined) { return undefined; } + return "[Channel" + this.activeDeck + "]"; + } + if (this.activeDeck === 1 || this.activeDeck === 2) { + if (group === "deck1") { return "[Channel1]"; } + if (group === "deck2") { return "[Channel2]"; } + } + if (this.activeDeck === 3 || this.activeDeck === 4) { + if (group === "deck1") { return "[Channel3]"; } + if (group === "deck2") { return "[Channel4]"; } + } + return undefined; } - if (this.activeDeck === 3 || this.activeDeck === 4) { - if (group === "deck1") { return "[Channel3]"; } - if (group === "deck2") { return "[Channel4]"; } + /** + * Find Output control matching give group and name + * + * @param {string} m_group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} m_name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @returns {HIDBitVector|any} Bitvector or bytewise field - Returns undefined if output field can't be found. + */ + getOutputField(m_group, m_name) { + for (const packet_name in this.OutputPackets) { + const packet = this.OutputPackets[packet_name]; + for (const group_name in packet.groups) { + const group = packet.groups[group_name]; + for (const field_name in group) { + const field = group[field_name]; + if (field.type === "bitvector") { + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; + if (bit.mapped_group === m_group && bit.mapped_name === m_name) { return bit; } + if (bit.group === m_group && bit.name === m_name) { return bit; } + } + continue; + } + if (field.mapped_group === m_group && field.mapped_name === m_name) { return field; } + if (field.group === m_group && field.name === m_name) { return field; } + } + } + } + return undefined; } - return undefined; -}; - -/** - * Find Output control matching give group and name - * - * @param {string} m_group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} m_name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @returns {HIDBitVector|any} Bitvector or bytewise field - Returns undefined if output field can't be found. - */ -HIDController.prototype.getOutputField = function(m_group, m_name) { - for (const packet_name in this.OutputPackets) { - const packet = this.OutputPackets[packet_name]; + /** + * 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 + */ + getInputPacket(name) { + if (!(name in this.InputPackets)) { return undefined; } + return this.InputPackets[name]; + } + /** + * 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 + */ + getOutputPacket(name) { + if (!(name in this.OutputPackets)) { return undefined; } + return this.OutputPackets[name]; + } + /** + * Set input packet callback afterwards + * + * @param {HIDPacket} packet The input packet + * @param {packetCallback} callback Callback function for the control + */ + setPacketCallback(packet, callback) { + const input_packet = this.getInputPacket(packet); + input_packet.callback = callback; + } + /** + * 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 {HIDPacket} packet The input packet + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {controlCallback} callback Callback function for the control + */ + setCallback(packet, group, name, callback) { + const input_packet = this.getInputPacket(packet); + if (input_packet === undefined) { + HIDDebug("Input packet not found " + packet); + return; + } + input_packet.setCallback(group, name, callback); + } + /** + * In almost every case, a HID controller sends data values with input fields which are not directly suitable for Mixxx control values. To solve this issue, HIDController contains function to scale the input value to suitable range automatically before calling any field processing functions. Scalers can be registered with HIDController.registerScalingFunction(group,name,callback) in HIDController. + * + * @callback scalingCallback + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} value Value to be scaled + * @returns {number} Scaled value + */ + /** + * 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 + */ + setScaler(name, callback) { + if (name in this.scalers) { return; } + this.scalers[name] = callback; + } + /** + * Lookup scaling function for control + * + * @param {string} name Reference of the scaling function in scalers list of HIDController + * @param _callback Unused + * @returns {scalingCallback} Scaling function. Returns undefined if function is not registered. + */ + getScaler(name, _callback) { + if (!(name in this.scalers)) { return undefined; } + return this.scalers[name]; + } + /** + * Change type of a previously defined field to modifier and register it + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param modifier + */ + linkModifier(group, name, modifier) { + const packet = this.getInputPacket(this.defaultPacket); + if (packet === undefined) { + HIDDebug( + "ERROR creating modifier: input packet " + this.defaultPacket + " not found" + ); + return; + } + const bit_id = group + "." + name; + const field = packet.lookupBit(group, name); + if (field === undefined) { + HIDDebug("BIT field not found: " + bit_id); + return; + } + field.group = "modifiers"; + field.name = modifier; + this.modifiers.set(modifier); + } + /** + * TODO - implement unlinking of modifiers + * + * @param {string} _group Unused + * @param {string} _name Unused + * @param _modifier Unused + */ + unlinkModifier(_group, _name, _modifier) { + HIDDebug("Unlinking of modifiers not yet implemented"); + } + /** + * Link a previously declared HID control to actual mixxx control + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} m_group Mapped group + * @param {string} m_name Mapped name + * @param {controlCallback} callback Callback function for the control + */ + linkControl(group, name, m_group, m_name, callback) { + let field; + const packet = this.getInputPacket(this.defaultPacket); + if (packet === undefined) { + HIDDebug("ERROR creating modifier: input packet " + this.defaultPacket + " not found"); + return; + } + field = packet.getField(group, name); + if (field === undefined) { + HIDDebug("Field not found: " + group + "." + name); + return; + } + if (field.type === "bitvector") { + field = packet.lookupBit(group, name); + if (field === undefined) { + HIDDebug("bit not found: " + group + "." + name); + return; + } + } + field.mapped_group = m_group; + field.mapped_name = m_name; + if (callback !== undefined) { field.callback = callback; } + } + /** + * TODO - implement unlinking of controls + * + * @param {string} _group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} _name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + */ + unlinkControl(_group, _name) { + } + /** + * Register HID input packet type to controller. + * 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) { + // Find modifiers and other special cases from packet fields for (const group_name in packet.groups) { const group = packet.groups[group_name]; for (const field_name in group) { const field = group[field_name]; + field.packet = packet; if (field.type === "bitvector") { for (const bit_id in field.value.bits) { const bit = field.value.bits[bit_id]; - if (bit.mapped_group === m_group && bit.mapped_name === m_name) { return bit; } - if (bit.group === m_group && bit.name === m_name) { return bit; } + bit.packet = packet; + if (bit.group === "modifiers") { this.modifiers.add(bit.name); } + } + } else { + if (field.group === "modifiers") { this.modifiers.add(field.name); } + } + } + } + this.InputPackets[packet.name] = packet; + } + /** + * Register HID output packet type to controller + * There are no special Output control output packets, just register Outputs to any + * valid packet and we detect them here. + * 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) { + this.OutputPackets[packet.name] = packet; + // Link packet to all fields + for (const group_name in packet.groups) { + const group = packet.groups[group_name]; + for (const field_name in group) { + const field = group[field_name]; + field.packet = packet; + if (field.type === "bitvector") { + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; + bit.packet = packet; } - continue; } - if (field.mapped_group === m_group && field.mapped_name === m_name) { return field; } - if (field.group === m_group && field.name === m_name) { return field; } } } } - return undefined; -}; - -/** - * 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 - */ -HIDController.prototype.getInputPacket = function(name) { - if (!(name in this.InputPackets)) { return undefined; } - return this.InputPackets[name]; -}; - -/** - * 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 - */ -HIDController.prototype.getOutputPacket = function(name) { - if (!(name in this.OutputPackets)) { return undefined; } - return this.OutputPackets[name]; -}; - -/** - * Set input packet callback afterwards - * - * @param {HIDPacket} packet The input packet - * @param {packetCallback} callback Callback function for the control - */ -HIDController.prototype.setPacketCallback = function(packet, callback) { - const input_packet = this.getInputPacket(packet); - input_packet.callback = callback; -}; - -/** - * 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 {HIDPacket} packet The input packet - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {controlCallback} callback Callback function for the control - */ -HIDController.prototype.setCallback = function(packet, group, name, callback) { - const input_packet = this.getInputPacket(packet); - if (input_packet === undefined) { - HIDDebug("Input packet not found " + packet); - return; - } - input_packet.setCallback(group, name, callback); -}; - -/** - * In almost every case, a HID controller sends data values with input fields which are not directly suitable for Mixxx control values. To solve this issue, HIDController contains function to scale the input value to suitable range automatically before calling any field processing functions. Scalers can be registered with HIDController.registerScalingFunction(group,name,callback) in HIDController. - * - * @callback scalingCallback - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {number} value Value to be scaled - * @returns {number} Scaled value - */ - -/** - * 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 - */ -HIDController.prototype.setScaler = function(name, callback) { - if (name in this.scalers) { return; } - this.scalers[name] = callback; -}; - -/** - * Lookup scaling function for control - * - * @param {string} name Reference of the scaling function in scalers list of HIDController - * @param _callback Unused - * @returns {scalingCallback} Scaling function. Returns undefined if function is not registered. - */ -HIDController.prototype.getScaler = function(name, _callback) { - if (!(name in this.scalers)) { return undefined; } - return this.scalers[name]; -}; - -/** - * Change type of a previously defined field to modifier and register it - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param modifier - */ -HIDController.prototype.linkModifier = function(group, name, modifier) { - const packet = this.getInputPacket(this.defaultPacket); - if (packet === undefined) { - HIDDebug( - "ERROR creating modifier: input packet " + this.defaultPacket + " not found" - ); - return; - } - const bit_id = group + "." + name; - const field = packet.lookupBit(group, name); - if (field === undefined) { - HIDDebug("BIT field not found: " + bit_id); - return; - } - field.group = "modifiers"; - field.name = modifier; - this.modifiers.set(modifier); -}; - -/** - * TODO - implement unlinking of modifiers - * - * @param {string} _group Unused - * @param {string} _name Unused - * @param _modifier Unused - */ -HIDController.prototype.unlinkModifier = function(_group, _name, _modifier) { - HIDDebug("Unlinking of modifiers not yet implemented"); -}; - -/** - * Link a previously declared HID control to actual mixxx control - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {string} m_group Mapped group - * @param {string} m_name Mapped name - * @param {controlCallback} callback Callback function for the control - */ -HIDController.prototype.linkControl = function(group, name, m_group, m_name, callback) { - let field; - const packet = this.getInputPacket(this.defaultPacket); - if (packet === undefined) { - HIDDebug("ERROR creating modifier: input packet " + this.defaultPacket + " not found"); - return; - } - field = packet.getField(group, name); - if (field === undefined) { - HIDDebug("Field not found: " + group + "." + name); - return; - } - if (field.type === "bitvector") { - field = packet.lookupBit(group, name); - if (field === undefined) { - HIDDebug("bit not found: " + group + "." + name); + /** + * 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 processDelta for results after processing automated fields + * + * @param {number[]} 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 + */ + parsePacket(data, length) { + /** @type {HIDPacket} */ + let packet; + let changed_data; + if (this.InputPackets === undefined) { return; } - } - field.mapped_group = m_group; - field.mapped_name = m_name; - if (callback !== undefined) { field.callback = callback; } -}; - -/** - * TODO - implement unlinking of controls - * - * @param {string} _group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} _name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - */ -HIDController.prototype.unlinkControl = function(_group, _name) { - -}; + for (const name in this.InputPackets) { + packet = this.InputPackets[name]; + + // When the device uses ReportIDs to enumerate the reports, hidapi + // prepends the report ID to the data sent to Mixxx. If the device + // only has a single report type, the HIDPacket constructor sets the + // reportId as 0. In this case, hidapi only sends the data of the + // report to Mixxx without a report ID. + if (packet.reportId !== 0 && packet.reportId !== data[0]) { + continue; + } -/** - * Register HID input packet type to controller. - * 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 - */ -HIDController.prototype.registerInputPacket = function(packet) { - // Find modifiers and other special cases from packet fields - for (const group_name in packet.groups) { - const group = packet.groups[group_name]; - for (const field_name in group) { - const field = group[field_name]; - field.packet = packet; - if (field.type === "bitvector") { - for (const bit_id in field.value.bits) { - const bit = field.value.bits[bit_id]; - bit.packet = packet; - if (bit.group === "modifiers") { this.modifiers.add(bit.name); } + if (packet.header !== undefined) { + for (let header_byte = 0; header_byte < packet.header.length; header_byte++) { + if (packet.header[header_byte] !== data[header_byte]) { + packet = undefined; + break; + } } - } else { - if (field.group === "modifiers") { this.modifiers.add(field.name); } + if (packet === undefined) { continue; } + } + changed_data = packet.parse(data); + if (packet.callback !== undefined) { + packet.callback(packet, changed_data); + return; } + // Process named group controls + if (packet.name === this.defaultPacket) { this.processIncomingPacket(packet, changed_data); } + // Process generic changed_data packet, if callback is defined + if (this.processDelta !== undefined) { this.processDelta(packet, changed_data); } + if (this.postProcessDelta !== undefined) { this.postProcessDelta(packet, changed_data); } + return; } - } - this.InputPackets[packet.name] = packet; -}; - -/** - * Register HID output packet type to controller - * There are no special Output control output packets, just register Outputs to any - * valid packet and we detect them here. - * 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 - */ -HIDController.prototype.registerOutputPacket = function(packet) { - this.OutputPackets[packet.name] = packet; - // Link packet to all fields - for (const group_name in packet.groups) { - const group = packet.groups[group_name]; - for (const field_name in group) { - const field = group[field_name]; - field.packet = packet; - if (field.type === "bitvector") { - for (const bit_id in field.value.bits) { - const bit = field.value.bits[bit_id]; - bit.packet = packet; - } + HIDDebug("Received unknown packet of " + length + " bytes"); + for (const i in data) { HIDDebug("BYTE " + data[i]); } + } + /** + * Process the modified field values (delta) from input packet fields for + * input control packet, if packet name is in this.defaultPacket. + * + * Button (Boolean value) field processing: + * - 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. + * + * 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. + * - 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. + * + * @param packet Unused + * @param delta + */ + processIncomingPacket(packet, delta) { + /** @type {packetField} */ + let field; + for (const name in delta) { + if (this.ignoredControlChanges !== undefined + && this.ignoredControlChanges.indexOf(name) !== -1) { continue; } + field = delta[name]; + if (field.type === "button") { + // Button/Boolean field + this.processButton(field); + } else if (field.type === "control") { + // Numeric value field + this.processControl(field); + } else { + HIDDebug("Unknown field " + field.name + " type " + field.type); } } } -}; - -/** - * 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 processDelta for results after processing automated fields - * - * @param {number[]} 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 - */ -HIDController.prototype.parsePacket = function(data, length) { - let packet; - let changed_data; - if (this.InputPackets === undefined) { - return; + /** + * Get active group for this field + * + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + * @returns {string} Group + */ + getActiveFieldGroup(field) { + if (field.mapped_group !== undefined) { + return this.resolveGroup(field.mapped_group); + } + const group = field.group; + if (group === undefined) { + if (this.activeDeck !== undefined) { return "[Channel" + this.activeDeck + "]"; } + } + if (this.valid_groups.indexOf(group) !== -1) { + //HIDDebug("Resolving group " + group); + return this.resolveGroup(group); + } + return group; } - for (const name in this.InputPackets) { - packet = this.InputPackets[name]; - - // When the device uses ReportIDs to enumerate the reports, hidapi - // prepends the report ID to the data sent to Mixxx. If the device - // only has a single report type, the HIDPacket constructor sets the - // reportId as 0. In this case, hidapi only sends the data of the - // report to Mixxx without a report ID. - if (packet.reportId !== 0 && packet.reportId !== data[0]) { - continue; + /** + * Get active control name from field + * + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + * @returns {string} Name of field + */ + getActiveFieldControl(field) { + if (field.mapped_name !== undefined) { return field.mapped_name; } + return field.name; + } + /** + * Process given button field, triggering events + * + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + */ + processButton(field) { + const group = this.getActiveFieldGroup(field); + const control = this.getActiveFieldControl(field); + + if (group === undefined) { + HIDDebug("processButton: Could not resolve group from " + + field.group + " " + field.mapped_group + " " + + field.name + " " + field.mapped_name + ); + return; } - if (packet.header !== undefined) { - for (let header_byte = 0; header_byte < packet.header.length; header_byte++) { - if (packet.header[header_byte] !== data[header_byte]) { - packet = undefined; - break; - } + if (group === "modifiers") { + if (field.value !== 0) { this.modifiers.set(control, true); } else { this.modifiers.set(control, false); } + return; + } + if (field.auto_repeat) { + const timer_id = "auto_repeat_" + field.id; + if (field.value) { + this.startAutoRepeatTimer(timer_id, field.auto_repeat_interval); + } else { + this.stopAutoRepeatTimer(timer_id); } - if (packet === undefined) { continue; } } - changed_data = packet.parse(data); - if (packet.callback !== undefined) { - packet.callback(packet, changed_data); + if (field.callback !== undefined) { + field.callback(field); return; } - // Process named group controls - if (packet.name === this.defaultPacket) { this.processIncomingPacket(packet, changed_data); } - // Process generic changed_data packet, if callback is defined - if (this.processDelta !== undefined) { this.processDelta(packet, changed_data); } - if (this.postProcessDelta !== undefined) { this.postProcessDelta(packet, changed_data); } - return; - } - HIDDebug("Received unknown packet of " + length + " bytes"); - for (const i in data) { HIDDebug("BYTE " + data[i]); } -}; - -/** - * Process the modified field values (delta) from input packet fields for - * input control packet, if packet name is in this.defaultPacket. - * - * Button (Boolean value) field processing: - * - 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. - * - * 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. - * - 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. - * - * @param packet Unused - * @param delta - */ -HIDController.prototype.processIncomingPacket = function(packet, delta) { - let field; - for (const name in delta) { - if (this.ignoredControlChanges !== undefined - && this.ignoredControlChanges.indexOf(name) !== -1) { continue; } - field = delta[name]; - if (field.type === "button") { - // Button/Boolean field - this.processButton(field); - } else if (field.type === "control") { - // Numeric value field - this.processControl(field); + if (control === "jog_touch") { + if (group !== undefined) { + if (field.value === this.buttonStates.pressed) { this.enableScratch(group, true); } else { this.enableScratch(group, false); } + } + return; + } + if (this.toggleButtons.indexOf(control) !== -1) { + if (field.value === this.buttonStates.released) { return; } + if (engine.getValue(group, control)) { + if (control === "play") { engine.setValue(group, "stop", true); } else { engine.setValue(group, control, false); } + } else { + engine.setValue(group, control, true); + } + return; + } + if (field.auto_repeat && field.value === this.buttonStates.pressed) { + HIDDebug("Callback for " + field.group); + engine.setValue(group, control, field.auto_repeat(field)); + } else if (engine.getValue(group, control) === false) { + engine.setValue(group, control, true); } else { - HIDDebug("Unknown field " + field.name + " type " + field.type); + engine.setValue(group, control, false); } } -}; - -/** - * Get active group for this field - * - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. - * @returns {string} Group - */ -HIDController.prototype.getActiveFieldGroup = function(field) { - if (field.mapped_group !== undefined) { - return this.resolveGroup(field.mapped_group); - } - const group = field.group; - if (group === undefined) { - if (this.activeDeck !== undefined) { return "[Channel" + this.activeDeck + "]"; } - } - if (this.valid_groups.indexOf(group) !== -1) { - //HIDDebug("Resolving group " + group); - return this.resolveGroup(group); - } - return group; -}; - -/** - * Get active control name from field - * - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. - * @returns {string} Name of field - */ -HIDController.prototype.getActiveFieldControl = function(field) { - if (field.mapped_name !== undefined) { return field.mapped_name; } - return field.name; -}; - -/** - * Process given button field, triggering events - * - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. - */ -HIDController.prototype.processButton = function(field) { - const group = this.getActiveFieldGroup(field); - const control = this.getActiveFieldControl(field); - - if (group === undefined) { - HIDDebug("processButton: Could not resolve group from " - + field.group + " " + field.mapped_group + " " - + field.name + " " + field.mapped_name - ); - return; - } + /** + * 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. + */ + processControl(field) { + let value; + const group = this.getActiveFieldGroup(field); + const control = this.getActiveFieldControl(field); + + if (group === undefined) { + HIDDebug("processControl: Could not resolve group from " + + field.group + " " + field.mapped_group + " " + + field.name + " " + field.mapped_name + ); + return; + } - if (group === "modifiers") { - if (field.value !== 0) { this.modifiers.set(control, true); } else { this.modifiers.set(control, false); } - return; - } - if (field.auto_repeat) { - const timer_id = "auto_repeat_" + field.id; - if (field.value) { - this.startAutoRepeatTimer(timer_id, field.auto_repeat_interval); - } else { - this.stopAutoRepeatTimer(timer_id); + if (field.callback !== undefined) { + value = field.callback(field); + return; } - } - if (field.callback !== undefined) { - field.callback(field); - return; - } - if (control === "jog_touch") { - if (group !== undefined) { - if (field.value === this.buttonStates.pressed) { this.enableScratch(group, true); } else { this.enableScratch(group, false); } + if (group === "modifiers") { + this.modifiers.set(control, field.value); + return; } - return; - } - if (this.toggleButtons.indexOf(control) !== -1) { - if (field.value === this.buttonStates.released) { return; } - if (engine.getValue(group, control)) { - if (control === "play") { engine.setValue(group, "stop", true); } else { engine.setValue(group, control, false); } - } else { - engine.setValue(group, control, true); + if (control === "jog_wheel") { + // Handle jog wheel scratching transparently + this.jog_wheel(field); + return; } - return; - } - if (field.auto_repeat && field.value === this.buttonStates.pressed) { - HIDDebug("Callback for " + field.group); - engine.setValue(group, control, field.auto_repeat(field)); - } else if (engine.getValue(group, control) === false) { - engine.setValue(group, control, true); - } else { - engine.setValue(group, control, false); - } -}; - -/** - * 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. - */ -HIDController.prototype.processControl = function(field) { - let value; - const group = this.getActiveFieldGroup(field); - const control = this.getActiveFieldControl(field); - - if (group === undefined) { - HIDDebug("processControl: Could not resolve group from " - + field.group + " " + field.mapped_group + " " - + field.name + " " + field.mapped_name - ); - return; - } - - if (field.callback !== undefined) { - value = field.callback(field); - return; - } - if (group === "modifiers") { - this.modifiers.set(control, field.value); - return; - } - if (control === "jog_wheel") { - // Handle jog wheel scratching transparently - this.jog_wheel(field); - return; - } - // Call value scaler if defined and send mixxx signal - value = field.value; - const scaler = this.getScaler(control); - if (field.isEncoder) { - let field_delta = field.delta; - if (scaler !== undefined) { field_delta = scaler(group, control, field_delta); } - engine.setValue(group, control, field_delta); - } else { - if (scaler !== undefined) { - value = scaler(group, control, value); - // See the Traktor S4 script for how to use this. If the scaler function has this - // parameter set to true, we use the effects-engine setParameter call instead of - // setValue. - if (scaler.useSetParameter) { - engine.setParameter(group, control, value); - return; + // Call value scaler if defined and send mixxx signal + value = field.value; + const scaler = this.getScaler(control); + if (field.isEncoder) { + let field_delta = field.delta; + if (scaler !== undefined) { field_delta = scaler(group, control, field_delta); } + engine.setValue(group, control, field_delta); + } else { + if (scaler !== undefined) { + value = scaler(group, control, value); + // See the Traktor S4 script for how to use this. If the scaler function has this + // parameter set to true, we use the effects-engine setParameter call instead of + // setValue. + if (scaler.useSetParameter) { + engine.setParameter(group, control, value); + return; + } } + engine.setValue(group, control, value); } - engine.setValue(group, control, value); - } -}; - -/** - * Toggle control state from toggle button - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param control - * @param value - */ -HIDController.prototype.toggle = function(group, control, value) { - if (value === this.buttonStates.released) { return; } - const status = engine.getValue(group, control) !== true; - engine.setValue(group, control, status); -}; - -/** - * Toggle play/pause state - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. - */ -HIDController.prototype.togglePlay = function(group, field) { - if (field.value === this.buttonStates.released) { return; } - const status = !(engine.getValue(group, "play")); - if (!status) { engine.setValue(group, "stop", true); } else { engine.setValue(group, "play", true); } -}; - -/** - * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {boolean} status Enable or Disable scratching: - * - true enables scratching (press 'jog_touch' button) - * Sets the internal 'isScratchEnabled' attribute to true, and calls scratchEnable - * with the scratch attributes (see class definition) - * - * - false disables scratching (release 'jog_touch' button) - * Sets the internal 'isScratchEnabled attribute to false, and calls scratchDisable - * to end scratching mode - */ -HIDController.prototype.enableScratch = function(group, status) { - const deck = this.resolveDeck(group); - if (status) { - this.isScratchEnabled = true; - engine.scratchEnable(deck, - this.scratchintervalsPerRev, - this.scratchRPM, - this.scratchAlpha, - this.scratchBeta, - this.rampedScratchEnable - ); - if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(true); } - } else { - this.isScratchEnabled = false; - engine.scratchDisable(deck, this.rampedScratchDisable); - if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(false); } - } -}; - -/** - * Default jog scratching function. Used to handle jog move events from special - * input control field called 'jog_wheel'. Handles both 'scratch' and 'jog' mixxx - * functions, depending on isScratchEnabled value above (see enableScratch()) - * - * Since most controllers require value scaling for jog and scratch functions, - * 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. - * 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. - * - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. - */ -HIDController.prototype.jog_wheel = function(field) { - let scaler = undefined; - const active_group = this.getActiveFieldGroup(field); - let value = undefined; - if (field.isEncoder) { value = field.delta; } else { value = field.value; } - if (this.isScratchEnabled) { - const deck = this.resolveDeck(active_group); - if (deck === undefined) { return; } - scaler = this.getScaler("jog_scratch"); - if (scaler !== undefined) { value = scaler(active_group, "jog_scratch", value); } else { HIDDebug("WARNING non jog_scratch scaler, you likely want one"); } - engine.scratchTick(deck, value); - } else { - if (active_group === undefined) { return; } - scaler = this.getScaler("jog"); - if (scaler !== undefined) { value = scaler(active_group, "jog", value); } else { HIDDebug("WARNING non jog scaler, you likely want one"); } - engine.setValue(active_group, "jog", value); - } -}; - -/** - * Stops the specified auto repeat timer - * - * @param {number} timer_id Reference of the timer to stop - */ -HIDController.prototype.stopAutoRepeatTimer = function(timer_id) { - if (this.timers[timer_id]) { - engine.stopTimer(this.timers[timer_id]); - delete this.timers[timer_id]; - } else { - //HIDDebug("No such autorepeat timer: " + timer_id); } -}; - -/** - * Toggle field autorepeat on or off - * - * @param {string} group - * @param {string} name - * @param {controlCallback} callback Callback function for the control - * @param {number} interval - */ -HIDController.prototype.setAutoRepeat = function(group, name, callback, interval) { - const packet = this.getInputPacket(this.defaultPacket); - const field = packet.getField(group, name); - if (field === undefined) { - HIDDebug("setAutoRepeat: field not found " + group + "." + name); - return; + /** + * Toggle control state from toggle button + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param control + * @param value + */ + toggle(group, control, value) { + if (value === this.buttonStates.released) { return; } + const status = engine.getValue(group, control) !== true; + engine.setValue(group, control, status); + } + /** + * Toggle play/pause state + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + */ + togglePlay(group, field) { + if (field.value === this.buttonStates.released) { return; } + const status = !(engine.getValue(group, "play")); + if (!status) { engine.setValue(group, "stop", true); } else { engine.setValue(group, "play", true); } + } + /** + * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {boolean} status Enable or Disable scratching: + * - true enables scratching (press 'jog_touch' button) + * Sets the internal 'isScratchEnabled' attribute to true, and calls scratchEnable + * with the scratch attributes (see class definition) + * + * - false disables scratching (release 'jog_touch' button) + * Sets the internal 'isScratchEnabled attribute to false, and calls scratchDisable + * to end scratching mode + */ + enableScratch(group, status) { + const deck = this.resolveDeck(group); + if (status) { + this.isScratchEnabled = true; + engine.scratchEnable(deck, + this.scratchintervalsPerRev, + this.scratchRPM, + this.scratchAlpha, + this.scratchBeta, + this.rampedScratchEnable + ); + if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(true); } + } else { + this.isScratchEnabled = false; + engine.scratchDisable(deck, this.rampedScratchDisable); + if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(false); } + } } - field.auto_repeat = callback; - if (interval) { field.auto_repeat_interval = interval; } else { field.auto_repeat_interval = controller.auto_repeat_interval; } - if (callback) { callback(field); } -}; - -/** - * Callback for auto repeat timer to send again the values for - * buttons and controls marked as 'auto_repeat' - * Timer must be defined from actual controller side, because of - * callback call namespaces and 'this' reference - */ -HIDController.prototype.autorepeatTimer = function() { - let group_name; - let group; - let field; - let field_name; - let bit_name; - let bit; - const packet = this.InputPackets[this.defaultPacket]; - for (group_name in packet.groups) { - group = packet.groups[group_name]; - for (field_name in group) { - field = group[field_name]; - if (field.type !== "bitvector") { - if (field.auto_repeat) { this.processControl(field); } - continue; - } - for (bit_name in field.value.bits) { - bit = field.value.bits[bit_name]; - if (bit.auto_repeat) { this.processButton(bit); } - } + /** + * Default jog scratching function. Used to handle jog move events from special + * input control field called 'jog_wheel'. Handles both 'scratch' and 'jog' mixxx + * functions, depending on isScratchEnabled value above (see enableScratch()) + * + * Since most controllers require value scaling for jog and scratch functions, + * 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. + * 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. + * + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + */ + jog_wheel(field) { + let scaler = undefined; + const active_group = this.getActiveFieldGroup(field); + let value = undefined; + if (field.isEncoder) { value = field.delta; } else { value = field.value; } + if (this.isScratchEnabled) { + const deck = this.resolveDeck(active_group); + if (deck === undefined) { return; } + scaler = this.getScaler("jog_scratch"); + if (scaler !== undefined) { value = scaler(active_group, "jog_scratch", value); } else { HIDDebug("WARNING non jog_scratch scaler, you likely want one"); } + engine.scratchTick(deck, value); + } else { + if (active_group === undefined) { return; } + scaler = this.getScaler("jog"); + if (scaler !== undefined) { value = scaler(active_group, "jog", value); } else { HIDDebug("WARNING non jog scaler, you likely want one"); } + engine.setValue(active_group, "jog", value); } } -}; - -/** - * Toggle active deck and update virtual output field control mappings - * - * @param {number} deck Number of deck - */ -HIDController.prototype.switchDeck = function(deck) { - let packet; - let field; - let controlgroup; - if (deck === undefined) { - if (this.activeDeck === undefined) { - deck = 1; + /** + * Stops the specified auto repeat timer + * + * @param {number} timer_id Reference of the timer to stop + */ + stopAutoRepeatTimer(timer_id) { + if (this.timers[timer_id]) { + engine.stopTimer(this.timers[timer_id]); + delete this.timers[timer_id]; } else { - // This is unusable: num_decks has always minimum 4 decks - // var totalDecks = engine.getValue("[Master]","num_decks"); - // deck = (this.activeDeck+1) % totalDecks; - deck = this.deckSwitchMap[this.activeDeck]; - if (deck === undefined) { deck = 1; } + //HIDDebug("No such autorepeat timer: " + timer_id); } } - const new_group = this.resolveDeckGroup(deck); - HIDDebug("Switching to deck " + deck + " group " + new_group); - if (this.disconnectDeck !== undefined) { this.disconnectDeck(); } - for (const packet_name in this.OutputPackets) { - packet = this.OutputPackets[packet_name]; - for (const group_name in packet.groups) { - const group = packet.groups[group_name]; - for (const field_name in group) { + /** + * Toggle field autorepeat on or off + * + * @param {string} group + * @param {string} name + * @param {controlCallback} callback Callback function for the control + * @param {number} interval + */ + setAutoRepeat(group, name, callback, interval) { + const packet = this.getInputPacket(this.defaultPacket); + const field = packet.getField(group, name); + if (field === undefined) { + HIDDebug("setAutoRepeat: field not found " + group + "." + name); + return; + } + field.auto_repeat = callback; + if (interval) { field.auto_repeat_interval = interval; } else { field.auto_repeat_interval = controller.auto_repeat_interval; } + if (callback) { callback(field); } + } + /** + * Callback for auto repeat timer to send again the values for + * buttons and controls marked as 'auto_repeat' + * Timer must be defined from actual controller side, because of + * callback call namespaces and 'this' reference + */ + autorepeatTimer() { + let group_name; + let group; + let field; + let field_name; + let bit_name; + let bit; + const packet = this.InputPackets[this.defaultPacket]; + for (group_name in packet.groups) { + group = packet.groups[group_name]; + for (field_name in group) { field = group[field_name]; - if (field.type === "bitvector") { - for (const bit_id in field.value.bits) { - const bit = field.value.bits[bit_id]; - if (this.virtualDecks.indexOf(bit.mapped_group) === -1) { continue; } - controlgroup = this.resolveGroup(bit.mapped_group); - engine.connectControl(controlgroup, bit.mapped_name, bit.mapped_callback, true); - engine.connectControl(new_group, bit.mapped_name, bit.mapped_callback); - var value = engine.getValue(new_group, bit.mapped_name); - HIDDebug("BIT " + bit.group + "." + bit.name + " value " + value); - if (value) { - this.setOutput( - bit.group, bit.name, - this.LEDColors[this.deckOutputColors[deck]] - ); - } else { - this.setOutput( - bit.group, bit.name, - this.LEDColors.off - ); - } - } + if (field.type !== "bitvector") { + if (field.auto_repeat) { this.processControl(field); } continue; } - // Only move outputs of virtual decks - if (this.virtualDecks.indexOf(field.mapped_group) === -1) { continue; } - controlgroup = this.resolveGroup(field.mapped_group); - engine.connectControl(controlgroup, field.mapped_name, field.mapped_callback, true); - engine.connectControl(new_group, field.mapped_name, field.mapped_callback); - value = engine.getValue(new_group, field.mapped_name); - if (value) { - this.setOutput( - field.group, field.name, - this.LEDColors[this.deckOutputColors[deck]] - ); - } else { - this.setOutput( - field.group, field.name, - this.LEDColors.off - ); + for (bit_name in field.value.bits) { + bit = field.value.bits[bit_name]; + if (bit.auto_repeat) { this.processButton(bit); } } } } } - this.activeDeck = deck; - if (this.connectDeck !== undefined) { this.connectDeck(); } -}; - -/** - * Link a virtual HID Output to mixxx control - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {string} m_group Mapped group - * @param {string} m_name Name of mapped control - * @param {controlCallback} callback Callback function for the control - */ -HIDController.prototype.linkOutput = function(group, name, m_group, m_name, callback) { - let controlgroup; - const field = this.getOutputField(group, name); - if (field === undefined) { - HIDDebug("Linked output not found: " + group + "." + name); - return; - } - if (field.mapped_group !== undefined) { - HIDDebug("Output already linked: " + field.mapped_group); - return; - } - controlgroup = this.resolveGroup(m_group); - field.mapped_group = m_group; - field.mapped_name = m_name; - field.mapped_callback = callback; - engine.connectControl(controlgroup, m_name, callback); - if (engine.getValue(controlgroup, m_name)) { this.setOutput(m_group, m_name, "on"); } else { this.setOutput(m_group, m_name, "off"); } -}; - -/** - * Unlink a virtual HID Output from mixxx control - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {controlCallback} callback Callback function for the control - */ -HIDController.prototype.unlinkOutput = function(group, name, callback) { - const field = this.getOutputField(group, name); - let controlgroup; - if (field === undefined) { - HIDDebug("Unlinked output not found: " + group + "." + name); - return; - } - if (field.mapped_group === undefined || field.mapped_name === undefined) { - HIDDebug("Unlinked output not mapped: " + group + "." + name); - return; - } - controlgroup = this.resolveGroup(field.mapped_group); - engine.connectControl(controlgroup, field.mapped_name, callback, true); - field.mapped_group = undefined; - field.mapped_name = undefined; - field.mapped_callback = undefined; -}; - -/** - * Set output state to given value - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {number} value Value to set as new output state of the control - * @param {boolean} send_packet If true, the packet (an HID OutputReport) is send immediately - */ -HIDController.prototype.setOutput = function(group, name, value, send_packet) { - const field = this.getOutputField(group, name); - if (field === undefined) { - HIDDebug("setOutput: unknown field: " + group + "." + name); - return; - } - field.value = value << field.bit_offset; - field.toggle = value << field.bit_offset; - if (send_packet) { field.packet.send(); } -}; - -/** - * Set Output to toggle between two values. Reset with setOutput(name,'off') - * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param toggle_value - */ -HIDController.prototype.setOutputToggle = function(group, name, toggle_value) { - const field = this.getOutputField(group, name); - if (field === undefined) { - HIDDebug("setOutputToggle: unknown field " + group + "." + name); - return; + /** + * Toggle active deck and update virtual output field control mappings + * + * @param {number} deck Number of deck + */ + switchDeck(deck) { + let packet; + let field; + let controlgroup; + if (deck === undefined) { + if (this.activeDeck === undefined) { + deck = 1; + } else { + // This is unusable: num_decks has always minimum 4 decks + // var totalDecks = engine.getValue("[Master]","num_decks"); + // deck = (this.activeDeck+1) % totalDecks; + deck = this.deckSwitchMap[this.activeDeck]; + if (deck === undefined) { deck = 1; } + } + } + const new_group = this.resolveDeckGroup(deck); + HIDDebug("Switching to deck " + deck + " group " + new_group); + if (this.disconnectDeck !== undefined) { this.disconnectDeck(); } + for (const packet_name in this.OutputPackets) { + packet = this.OutputPackets[packet_name]; + for (const group_name in packet.groups) { + const group = packet.groups[group_name]; + for (const field_name in group) { + field = group[field_name]; + if (field.type === "bitvector") { + for (const bit_id in field.value.bits) { + const bit = field.value.bits[bit_id]; + if (this.virtualDecks.indexOf(bit.mapped_group) === -1) { continue; } + controlgroup = this.resolveGroup(bit.mapped_group); + engine.connectControl(controlgroup, bit.mapped_name, bit.mapped_callback, true); + engine.connectControl(new_group, bit.mapped_name, bit.mapped_callback); + var value = engine.getValue(new_group, bit.mapped_name); + HIDDebug("BIT " + bit.group + "." + bit.name + " value " + value); + if (value) { + this.setOutput( + bit.group, bit.name, + this.LEDColors[this.deckOutputColors[deck]] + ); + } else { + this.setOutput( + bit.group, bit.name, + this.LEDColors.off + ); + } + } + continue; + } + // Only move outputs of virtual decks + if (this.virtualDecks.indexOf(field.mapped_group) === -1) { continue; } + controlgroup = this.resolveGroup(field.mapped_group); + engine.connectControl(controlgroup, field.mapped_name, field.mapped_callback, true); + engine.connectControl(new_group, field.mapped_name, field.mapped_callback); + value = engine.getValue(new_group, field.mapped_name); + if (value) { + this.setOutput( + field.group, field.name, + this.LEDColors[this.deckOutputColors[deck]] + ); + } else { + this.setOutput( + field.group, field.name, + this.LEDColors.off + ); + } + } + } + } + this.activeDeck = deck; + if (this.connectDeck !== undefined) { this.connectDeck(); } + } + /** + * Link a virtual HID Output to mixxx control + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} m_group Mapped group + * @param {string} m_name Name of mapped control + * @param {controlCallback} callback Callback function for the control + */ + linkOutput(group, name, m_group, m_name, callback) { + let controlgroup; + const field = this.getOutputField(group, name); + if (field === undefined) { + HIDDebug("Linked output not found: " + group + "." + name); + return; + } + if (field.mapped_group !== undefined) { + HIDDebug("Output already linked: " + field.mapped_group); + return; + } + controlgroup = this.resolveGroup(m_group); + field.mapped_group = m_group; + field.mapped_name = m_name; + field.mapped_callback = callback; + engine.connectControl(controlgroup, m_name, callback); + if (engine.getValue(controlgroup, m_name)) { this.setOutput(m_group, m_name, "on"); } else { this.setOutput(m_group, m_name, "off"); } + } + /** + * Unlink a virtual HID Output from mixxx control + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {controlCallback} callback Callback function for the control + */ + unlinkOutput(group, name, callback) { + const field = this.getOutputField(group, name); + let controlgroup; + if (field === undefined) { + HIDDebug("Unlinked output not found: " + group + "." + name); + return; + } + if (field.mapped_group === undefined || field.mapped_name === undefined) { + HIDDebug("Unlinked output not mapped: " + group + "." + name); + return; + } + controlgroup = this.resolveGroup(field.mapped_group); + engine.connectControl(controlgroup, field.mapped_name, callback, true); + field.mapped_group = undefined; + field.mapped_name = undefined; + field.mapped_callback = undefined; + } + /** + * Set output state to given value + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} value Value to set as new output state of the control + * @param {boolean} send_packet If true, the packet (an HID OutputReport) is send immediately + */ + setOutput(group, name, value, send_packet) { + const field = this.getOutputField(group, name); + if (field === undefined) { + HIDDebug("setOutput: unknown field: " + group + "." + name); + return; + } + field.value = value << field.bit_offset; + field.toggle = value << field.bit_offset; + if (send_packet) { field.packet.send(); } + } + /** + * Set Output to toggle between two values. Reset with setOutput(name,'off') + * + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param toggle_value + */ + setOutputToggle(group, name, toggle_value) { + const field = this.getOutputField(group, name); + if (field === undefined) { + HIDDebug("setOutputToggle: unknown field " + group + "." + name); + return; + } + field.value = toggle_value << field.bit_offset; + field.toggle = toggle_value << field.bit_offset; + field.packet.send(); } - field.value = toggle_value << field.bit_offset; - field.toggle = toggle_value << field.bit_offset; - field.packet.send(); -}; +} +// Add class HIDController to the Global JavaScript object +this.HIDController = HIDController; From 10139a1a6628ba02b360cd8c98d7108c26919f97 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 30 Apr 2022 10:46:25 +0200 Subject: [PATCH 04/60] Documentation fixes --- res/controllers/common-hid-packet-parser.js | 100 +++++++++++--------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index d896342e3f8..672c5690b86 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -5,10 +5,33 @@ * * @param {string} message Message to be printed on controller debug console output */ + var HIDDebug = function(message) { print("HID " + message); }; - +/** + * Callback function to call when, the packet represents an HID InputReport, and new data for this 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 {number[]} changed_data The data received from the device + */ +/** + * Callback function to call when, data for specified filed in the packet is updated. + * + * @callback controlCallback + * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + */ +/** + * In almost every case, a HID controller sends data values with input fields which are not directly suitable for Mixxx control values. To solve this issue, HIDController contains function to scale the input value to suitable range automatically before calling any field processing functions. Scalers can be registered with HIDController.registerScalingFunction(group,name,callback) in HIDController. + * + * @callback scalingCallback + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {number} value Value to be scaled + * @returns {number} Scaled value + */ /** * @typedef packetField * @type {object} @@ -26,8 +49,15 @@ var HIDDebug = function(message) { * @property {controlCallback} callback * @property {boolean} soft_takeover * @property {boolean} ignored - * @property {boolean} auto_repeat + * @property {controlCallback} auto_repeat * @property {number} auto_repeat_interval + * @property {number} min + * @property {number} max + * @property {string} type + * @property {HIDBitVector|boolean|number} value + * @property {number} delta + * @property {number} mindelta + * @property {number} toggle */ /** @@ -57,7 +87,7 @@ class HIDBitVector { /** * Add a control bitmask to the HIDBitVector * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control without any additional code. Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. */ @@ -80,9 +110,9 @@ class HIDBitVector { this.bits[bit.id] = bit; } /** - * Add a Output control bitmask to the HIDBitVector + * Add an output control bitmask to the HIDBitVector * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to an output without any additional code. * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. */ @@ -134,7 +164,7 @@ class HIDModifierList { * Set modifier value * * @param {string} name Name of modifier - * @param {number} value Value to be set + * @param {number|boolean} value Value to be set */ set(name, value) { if (!(name in this.modifiers)) { @@ -151,7 +181,7 @@ class HIDModifierList { * Get modifier value * * @param {string} name Name of modifier - * @returns {number} Value of modifier + * @returns {number|boolean} Value of modifier */ get(name) { if (!(name in this.modifiers)) { @@ -161,18 +191,10 @@ class HIDModifierList { return this.modifiers[name]; } /** - * Callback function to call when, the packet represents an HID InputReport, and new data for this 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 {number[]} changed_data The data received from the device - */ - /** - * Set modifier callback (update function after modifier state changes) + * Set modifier callback function * * @param {string} name Name of reference in HIDModifierList - * @param {packetCallback} callback Function to be called when value changed + * @param {packetCallback} callback Function to be called after modifier value changes */ setCallback(name, callback) { if (!(name in this.modifiers)) { @@ -194,8 +216,7 @@ this.HIDModifierList = HIDModifierList; * Each HIDPacket must be registered to HIDController. * * @param {string} name Name of packet (it makes sense to refer the HID report type and HID Report-ID here e.g. 'InputReport_0x02' or 'OutputReport_0x81') - * @param {number} reportId report ID of the packet. If the device only uses - * one report type, this must be 0. + * @param {number} reportId ReportID of the packet. If the device does not use ReportIDs this must be 0. * @param {packetCallback} callback function to call when the packet type represents an InputReport an a new report is received. If packet callback is set, the * packet is not parsed by delta functions. * callback is not meaningful for output packets @@ -225,7 +246,7 @@ class HIDPacket { * Pack a field value to the packet. * Can only pack bits and byte values, patches welcome. * - * @param {number} data Data received as InputReport from the device + * @param {number[]} 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. */ pack(data, field) { @@ -282,7 +303,7 @@ class HIDPacket { * - i signed integer * - I unsigned integer * - * @param {number} data Data received as InputReport from the device + * @param {number[]} 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 */ @@ -312,8 +333,8 @@ class HIDPacket { * Create group if create is true * * @param {string} name Name of the group - * @param {boolean} create If true, group will be created - @returns {string} Group Returns group or undefined, when group is not existing and create is set to false + * @param {boolean} [create=false] If true, group will be created + @returns {any} Group Returns group or undefined, when group is not existing and create is set to false */ getGroup(name, create) { if (this.groups === undefined) { this.groups = {}; } @@ -402,7 +423,7 @@ class HIDPacket { * * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @returns {HIDBitVector} Reference to a bit in a bitvector field + * @returns {packetField} Reference to a bit in a bitvector field */ lookupBit(group, name) { const field = this.getField(group, name); @@ -432,12 +453,6 @@ class HIDPacket { } delete control_group[name]; } - /** - * Callback function to call when, data for specified filed in the packet is updated. - * - * @callback controlCallback - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. - */ /** * Register a numeric value to parse from input packet * @@ -571,6 +586,7 @@ class HIDPacket { */ addOutput(group, name, offset, pack, bitmask, callback) { const control_group = this.getGroup(group, true); + /** @type {packetField} */ let field; let bitvector = undefined; const field_id = group + "." + name; @@ -737,7 +753,7 @@ class HIDPacket { * Data is expected to be a Packet() received from HID device. * BitVectors are returned as bits you can iterate separately. * - * @param {number} data Data received as InputReport from the device + * @param {number[]} data Data received as InputReport from the device * @returns List of changed fields with new value. */ parse(data) { @@ -869,6 +885,9 @@ this.HIDPacket = HIDPacket; * @property {number} scratchBeta Beta value for scratch_enable * @property {boolean} scratchRampOnEnable UNUSED If 'ramp' is used when enabling scratch * @property {boolean} scratchRampOnDisable UNUSED If 'ramp' is used when disabling scratch + * @property {boolean} rampedScratchEnable + * @property {boolean} rampedScratchDisable + * @property {any} enableScratchCallback */ class HIDController { constructor() { @@ -1052,7 +1071,7 @@ class HIDController { /** * Set input packet callback afterwards * - * @param {HIDPacket} packet The input packet + * @param {string} packet The name of the input packet e.g. 'InputReport_0x02' * @param {packetCallback} callback Callback function for the control */ setPacketCallback(packet, callback) { @@ -1064,7 +1083,7 @@ class HIDController { * If packet has callback, it is still parsed but no field processing is done, * callback is called directly after unpacking fields from packet. * - * @param {HIDPacket} packet The input packet + * @param {string} packet The name of the input packet e.g. 'InputReport_0x02' * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) * @param {controlCallback} callback Callback function for the control @@ -1077,15 +1096,6 @@ class HIDController { } input_packet.setCallback(group, name, callback); } - /** - * In almost every case, a HID controller sends data values with input fields which are not directly suitable for Mixxx control values. To solve this issue, HIDController contains function to scale the input value to suitable range automatically before calling any field processing functions. Scalers can be registered with HIDController.registerScalingFunction(group,name,callback) in HIDController. - * - * @callback scalingCallback - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {number} value Value to be scaled - * @returns {number} Scaled value - */ /** * Register scaling function for a control name * This does not check if given control name is valid @@ -1113,7 +1123,7 @@ class HIDController { * * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param modifier + * @param {any} modifier */ linkModifier(group, name, modifier) { const packet = this.getInputPacket(this.defaultPacket); @@ -1566,7 +1576,7 @@ class HIDController { /** * Stops the specified auto repeat timer * - * @param {number} timer_id Reference of the timer to stop + * @param {string} timer_id Reference of the timer to stop */ stopAutoRepeatTimer(timer_id) { if (this.timers[timer_id]) { @@ -1756,7 +1766,7 @@ class HIDController { * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) * @param {number} value Value to set as new output state of the control - * @param {boolean} send_packet If true, the packet (an HID OutputReport) is send immediately + * @param {boolean} [send_packet=false] If true, the packet (an HID OutputReport) is send immediately */ setOutput(group, name, value, send_packet) { const field = this.getOutputField(group, name); From 702986b8e1ddb47408a1e4b5d85aa05b6f4d83ba Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 1 May 2022 19:07:32 +0200 Subject: [PATCH 05/60] Minor documentation corrections --- res/controllers/common-hid-packet-parser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 672c5690b86..52f6c90c1c2 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -3,7 +3,7 @@ /** * Common HID script debugging function. Just to get logging with 'HID' prefix. * - * @param {string} message Message to be printed on controller debug console output + * @param {any} message Message to be printed on controller debug console output */ var HIDDebug = function(message) { @@ -582,7 +582,7 @@ class HIDPacket { * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 * @param pack control packing format for pack(), one of b/B, h/H, i/I * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. - * @param {controlCallback} callback Callback function for the control + * @param {controlCallback} [callback=undefined] Callback function for the control */ addOutput(group, name, offset, pack, bitmask, callback) { const control_group = this.getGroup(group, true); From 1d6b9761bffb13015e5bae92350fc3ef5c9be042 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Mon, 6 Jun 2022 09:27:09 +0200 Subject: [PATCH 06/60] Added missing definitions/default/comment --- res/controllers/common-hid-packet-parser.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 52f6c90c1c2..e7453cb2e1c 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -41,6 +41,7 @@ var HIDDebug = function(message) { * @property {string} name * @property {string} mapped_group * @property {string} mapped_name + * @property {controlCallback} mapped_callback * @property {object} pack Control packing format for unpack(), one of b/B, h/H, i/I * @property {number} offset * @property {number} end_offset @@ -818,7 +819,7 @@ class HIDPacket { * field object values are packed to the HID packet according to the * field type. * - * @param {boolean} debug Enables debug output to console + * @param {boolean} [debug=false] Enables debug output to console */ send(debug) { const data = []; @@ -888,6 +889,7 @@ this.HIDPacket = HIDPacket; * @property {boolean} rampedScratchEnable * @property {boolean} rampedScratchDisable * @property {any} enableScratchCallback + * @property {number} auto_repeat_interval Auto repeat interval default for fields, where not specified individual */ class HIDController { constructor() { @@ -1022,7 +1024,7 @@ class HIDController { * * @param {string} m_group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param {string} m_name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @returns {HIDBitVector|any} Bitvector or bytewise field - Returns undefined if output field can't be found. + * @returns {packetField|any} Bitvector or bytewise field - Returns undefined if output field can't be found. */ getOutputField(m_group, m_name) { for (const packet_name in this.OutputPackets) { From b890d51c026ed4d45ddc755a9635dc0533821ea1 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Mon, 6 Jun 2022 12:35:20 +0200 Subject: [PATCH 07/60] Added missing type for bit to JSDoc Removed one duplicate line --- res/controllers/common-hid-packet-parser.js | 34 +++++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index e7453cb2e1c..24fd2c37422 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -54,19 +54,46 @@ var HIDDebug = function(message) { * @property {number} auto_repeat_interval * @property {number} min * @property {number} max - * @property {string} type + * @property {('bitvector'|'button'|'control'|'output')} type Must be either: + * - 'bitvector' If value is of type HIDBitVector + * - 'button' If value is a boolean + * - 'control' If value is a number + * - 'output' * @property {HIDBitVector|boolean|number} value * @property {number} delta * @property {number} mindelta * @property {number} toggle */ +/** + * @typedef bitObject + * @type {object} + * @property {HIDPacket} packet + * @property {string} id Group and control name separated by a dot + * @property {string} group + * @property {string} name + * @property {string} mapped_group + * @property {string} mapped_name + * @property {number} bitmask + * @property {number} bit_offset + * @property {controlCallback} callback + * @property {controlCallback} auto_repeat + * @property {number} auto_repeat_interval + * @property {('button'|'output')} type Must be either: + * - 'button' + * - 'output' + * @property {boolean} value + */ + /** * HID Bit Vector Class * * Collection of bits in one parsed packet field. These objects are * created by HIDPacket addControl and addOutput and should not be * created manually. + * + * @property {number} size + * @property {bitObject{}} bits */ class HIDBitVector { constructor() { @@ -93,6 +120,7 @@ class HIDBitVector { * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. */ addBitMask(group, name, bitmask) { + /** @type {bitObject} */ const bit = {}; bit.type = "button"; bit.packet = undefined; @@ -102,7 +130,6 @@ class HIDBitVector { bit.mapped_group = undefined; bit.mapped_name = undefined; bit.bitmask = bitmask; - bit.bitmask = bitmask; bit.bit_offset = this.getOffset(bitmask); bit.callback = undefined; bit.value = undefined; @@ -118,6 +145,7 @@ class HIDBitVector { * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. */ addOutputMask(group, name, bitmask) { + /** @type {bitObject} */ const bit = {}; bit.type = "output"; bit.packet = undefined; @@ -1024,7 +1052,7 @@ class HIDController { * * @param {string} m_group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param {string} m_name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @returns {packetField|any} Bitvector or bytewise field - Returns undefined if output field can't be found. + * @returns {bitObject|packetField} Bit or bytewise field - Returns undefined if output field can't be found. */ getOutputField(m_group, m_name) { for (const packet_name in this.OutputPackets) { From d20868e53b84d8fc7cac7f7381fdf8bfb7bc077a Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 9 Jul 2022 16:57:34 +0200 Subject: [PATCH 08/60] Made hard understandable code section better readable --- res/controllers/common-hid-packet-parser.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 24fd2c37422..f7e20a026d2 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -108,7 +108,9 @@ class HIDBitVector { */ getOffset(bitmask) { for (let i = 0; i < 32; i++) { - if ((1 & bitmask >> i) !== 0) { return i; } + if ((bitmask >> i) & 1) { + return i; + } } return 0; } From 5908c8ecfababdba2bd89182a613ebe7dbef5fa6 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 9 Jul 2022 17:24:25 +0200 Subject: [PATCH 09/60] Reformated many if statements with line breaks, for better code readability --- res/controllers/common-hid-packet-parser.js | 171 +++++++++++++++----- 1 file changed, 132 insertions(+), 39 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index f7e20a026d2..85f30bb4f55 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -288,7 +288,9 @@ class HIDPacket { } const bytes = this.packSizes[field.pack]; let signed = false; - if (this.signedPackFormats.indexOf(field.pack) !== -1) { signed = true; } + if (this.signedPackFormats.indexOf(field.pack) !== -1) { + signed = true; + } if (field.type === "bitvector") { // TODO - fix multi byte bit vector outputs @@ -347,15 +349,23 @@ class HIDPacket { } const bytes = this.packSizes[field.pack]; let signed = false; - if (this.signedPackFormats.indexOf(field.pack) !== -1) { signed = true; } + if (this.signedPackFormats.indexOf(field.pack) !== -1) { + signed = true; + } for (let field_byte = 0; field_byte < bytes; field_byte++) { - if (data[field.offset + field_byte] === 255 && field_byte === 4) { value += 0; } else { value += data[field.offset + field_byte] * Math.pow(2, (field_byte * 8)); } + if (data[field.offset + field_byte] === 255 && field_byte === 4) { + value += 0; + } else { + value += data[field.offset + field_byte] * Math.pow(2, (field_byte * 8)); + } } if (signed) { const max_value = Math.pow(2, bytes * 8); const split = max_value / 2 - 1; - if (value > split) { value = value - max_value; } + if (value > split) { + value = value - max_value; + } } return value; } @@ -368,9 +378,15 @@ class HIDPacket { @returns {any} Group Returns group or undefined, when group is not existing and create is set to false */ getGroup(name, create) { - if (this.groups === undefined) { this.groups = {}; } - if (name in this.groups) { return this.groups[name]; } - if (!create) { return undefined; } + if (this.groups === undefined) { + this.groups = {}; + } + if (name in this.groups) { + return this.groups[name]; + } + if (!create) { + return undefined; + } this.groups[name] = {}; return this.groups[name]; } @@ -437,7 +453,9 @@ class HIDPacket { control_group = this.groups[group_name]; for (const field_name in control_group) { const field = control_group[field_name]; - if (field === undefined || field.type !== "bitvector") { continue; } + if (field === undefined || field.type !== "bitvector") { + continue; + } for (const bit_name in field.value.bits) { const bit = field.value.bits[bit_name]; if (bit.id === field_id) { @@ -465,7 +483,9 @@ class HIDPacket { const bit_id = group + "." + name; for (const bit_name in field.value.bits) { const bit = field.value.bits[bit_name]; - if (bit.id === bit_id) { return bit; } + if (bit.id === bit_id) { + return bit; + } } HIDDebug("BUG: bit not found after successful field lookup"); return undefined; @@ -715,7 +735,9 @@ class HIDPacket { if (field.type === "bitvector") { for (const bit_id in field.value.bits) { const bit = field.value.bits[bit_id]; - if (bit_id !== field_id) { continue; } + if (bit_id !== field_id) { + continue; + } bit.callback = callback; return; } @@ -774,7 +796,9 @@ class HIDPacket { for (const bit_id in field.value.bits) { bit = field.value.bits[bit_id]; new_value = (bit.bitmask & value) >> bit.bit_offset; - if (bit.value !== undefined && bit.value !== new_value) { bits[bit_id] = bit; } + if (bit.value !== undefined && bit.value !== new_value) { + bits[bit_id] = bit; + } bit.value = new_value; } return bits; @@ -798,7 +822,9 @@ class HIDPacket { group = this.groups[group_name]; for (field_id in group) { field = group[field_id]; - if (field === undefined) { continue; } + if (field === undefined) { + continue; + } const value = this.unpack(data, field); if (value === undefined) { @@ -812,7 +838,9 @@ class HIDPacket { for (const bit_name in changed_bits) { field_changes[bit_name] = changed_bits[bit_name]; } } else if (field.type === "control") { - if (field.value === value && field.mindelta !== undefined) { continue; } + if (field.value === value && field.mindelta !== undefined) { + continue; + } if (field.ignored || field.value === undefined) { field.value = value; continue; @@ -987,7 +1015,9 @@ class HIDController { close() { for (const name in this.timers) { const timer = this.timers[name]; - if (timer === undefined) { continue; } + if (timer === undefined) { + continue; + } engine.stopTimer(timer); this.timers[name] = undefined; } @@ -1031,12 +1061,16 @@ class HIDController { */ resolveGroup(group) { const channel_name = /\[Channel[0-9]+\]/; - if (group !== undefined && group.match(channel_name)) { return group; } + if (group !== undefined && group.match(channel_name)) { + return group; + } if (this.valid_groups.indexOf(group) !== -1) { return group; } if (group === "deck" || group === undefined) { - if (this.activeDeck === undefined) { return undefined; } + if (this.activeDeck === undefined) { + return undefined; + } return "[Channel" + this.activeDeck + "]"; } if (this.activeDeck === 1 || this.activeDeck === 2) { @@ -1086,7 +1120,9 @@ class HIDController { * @returns {HIDPacket} The input packet */ getInputPacket(name) { - if (!(name in this.InputPackets)) { return undefined; } + if (!(name in this.InputPackets)) { + return undefined; + } return this.InputPackets[name]; } /** @@ -1097,7 +1133,9 @@ class HIDController { * @returns {HIDPacket} The output packet */ getOutputPacket(name) { - if (!(name in this.OutputPackets)) { return undefined; } + if (!(name in this.OutputPackets)) { + return undefined; + } return this.OutputPackets[name]; } /** @@ -1136,7 +1174,9 @@ class HIDController { * @param {scalingCallback} callback Scaling function */ setScaler(name, callback) { - if (name in this.scalers) { return; } + if (name in this.scalers) { + return; + } this.scalers[name] = callback; } /** @@ -1147,7 +1187,9 @@ class HIDController { * @returns {scalingCallback} Scaling function. Returns undefined if function is not registered. */ getScaler(name, _callback) { - if (!(name in this.scalers)) { return undefined; } + if (!(name in this.scalers)) { + return undefined; + } return this.scalers[name]; } /** @@ -1215,7 +1257,9 @@ class HIDController { } field.mapped_group = m_group; field.mapped_name = m_name; - if (callback !== undefined) { field.callback = callback; } + if (callback !== undefined) { + field.callback = callback; + } } /** * TODO - implement unlinking of controls @@ -1359,8 +1403,10 @@ class HIDController { /** @type {packetField} */ let field; for (const name in delta) { - if (this.ignoredControlChanges !== undefined - && this.ignoredControlChanges.indexOf(name) !== -1) { continue; } + if (this.ignoredControlChanges !== undefined && + this.ignoredControlChanges.indexOf(name) !== -1) { + continue; + } field = delta[name]; if (field.type === "button") { // Button/Boolean field @@ -1385,7 +1431,9 @@ class HIDController { } const group = field.group; if (group === undefined) { - if (this.activeDeck !== undefined) { return "[Channel" + this.activeDeck + "]"; } + if (this.activeDeck !== undefined) { + return "[Channel" + this.activeDeck + "]"; + } } if (this.valid_groups.indexOf(group) !== -1) { //HIDDebug("Resolving group " + group); @@ -1400,8 +1448,11 @@ class HIDController { * @returns {string} Name of field */ getActiveFieldControl(field) { - if (field.mapped_name !== undefined) { return field.mapped_name; } - return field.name; + if (field.mapped_name !== undefined) { + return field.mapped_name; + } else { + return field.name; + } } /** * Process given button field, triggering events @@ -1421,7 +1472,11 @@ class HIDController { } if (group === "modifiers") { - if (field.value !== 0) { this.modifiers.set(control, true); } else { this.modifiers.set(control, false); } + if (field.value !== 0) { + this.modifiers.set(control, true); + } else { + this.modifiers.set(control, false); + } return; } if (field.auto_repeat) { @@ -1438,14 +1493,22 @@ class HIDController { } if (control === "jog_touch") { if (group !== undefined) { - if (field.value === this.buttonStates.pressed) { this.enableScratch(group, true); } else { this.enableScratch(group, false); } + if (field.value === this.buttonStates.pressed) { + this.enableScratch(group, true); + } else { + this.enableScratch(group, false); + } } return; } if (this.toggleButtons.indexOf(control) !== -1) { if (field.value === this.buttonStates.released) { return; } if (engine.getValue(group, control)) { - if (control === "play") { engine.setValue(group, "stop", true); } else { engine.setValue(group, control, false); } + if (control === "play") { + engine.setValue(group, "stop", true); + } else { + engine.setValue(group, control, false); + } } else { engine.setValue(group, control, true); } @@ -1496,7 +1559,9 @@ class HIDController { const scaler = this.getScaler(control); if (field.isEncoder) { let field_delta = field.delta; - if (scaler !== undefined) { field_delta = scaler(group, control, field_delta); } + if (scaler !== undefined) { + field_delta = scaler(group, control, field_delta); + } engine.setValue(group, control, field_delta); } else { if (scaler !== undefined) { @@ -1520,7 +1585,9 @@ class HIDController { * @param value */ toggle(group, control, value) { - if (value === this.buttonStates.released) { return; } + if (value === this.buttonStates.released) { + return; + } const status = engine.getValue(group, control) !== true; engine.setValue(group, control, status); } @@ -1531,9 +1598,15 @@ class HIDController { * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. */ togglePlay(group, field) { - if (field.value === this.buttonStates.released) { return; } + if (field.value === this.buttonStates.released) { + return; + } const status = !(engine.getValue(group, "play")); - if (!status) { engine.setValue(group, "stop", true); } else { engine.setValue(group, "play", true); } + if (!status) { + engine.setValue(group, "stop", true); + } else { + engine.setValue(group, "play", true); + } } /** * Processing of the 'jog_touch' special button name, which is used to detect @@ -1596,12 +1669,20 @@ class HIDController { const deck = this.resolveDeck(active_group); if (deck === undefined) { return; } scaler = this.getScaler("jog_scratch"); - if (scaler !== undefined) { value = scaler(active_group, "jog_scratch", value); } else { HIDDebug("WARNING non jog_scratch scaler, you likely want one"); } + if (scaler !== undefined) { + value = scaler(active_group, "jog_scratch", value); + } else { + HIDDebug("WARNING non jog_scratch scaler, you likely want one"); + } engine.scratchTick(deck, value); } else { if (active_group === undefined) { return; } scaler = this.getScaler("jog"); - if (scaler !== undefined) { value = scaler(active_group, "jog", value); } else { HIDDebug("WARNING non jog scaler, you likely want one"); } + if (scaler !== undefined) { + value = scaler(active_group, "jog", value); + } else { + HIDDebug("WARNING non jog scaler, you likely want one"); + } engine.setValue(active_group, "jog", value); } } @@ -1634,7 +1715,11 @@ class HIDController { return; } field.auto_repeat = callback; - if (interval) { field.auto_repeat_interval = interval; } else { field.auto_repeat_interval = controller.auto_repeat_interval; } + if (interval) { + field.auto_repeat_interval = interval; + } else { + field.auto_repeat_interval = controller.auto_repeat_interval; + } if (callback) { callback(field); } } /** @@ -1739,7 +1824,9 @@ class HIDController { } } this.activeDeck = deck; - if (this.connectDeck !== undefined) { this.connectDeck(); } + if (this.connectDeck !== undefined) { + this.connectDeck(); + } } /** * Link a virtual HID Output to mixxx control @@ -1766,7 +1853,11 @@ class HIDController { field.mapped_name = m_name; field.mapped_callback = callback; engine.connectControl(controlgroup, m_name, callback); - if (engine.getValue(controlgroup, m_name)) { this.setOutput(m_group, m_name, "on"); } else { this.setOutput(m_group, m_name, "off"); } + if (engine.getValue(controlgroup, m_name)) { + this.setOutput(m_group, m_name, "on"); + } else { + this.setOutput(m_group, m_name, "off"); + } } /** * Unlink a virtual HID Output from mixxx control @@ -1808,7 +1899,9 @@ class HIDController { } field.value = value << field.bit_offset; field.toggle = value << field.bit_offset; - if (send_packet) { field.packet.send(); } + if (send_packet) { + field.packet.send(); + } } /** * Set Output to toggle between two values. Reset with setOutput(name,'off') From 2334fbef31fbda4e7fe035bcd5102b0299826169 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 9 Jul 2022 17:49:04 +0200 Subject: [PATCH 10/60] Marked HIDDebug as deprecated and replaced the also deprecated print in it's implementation by console.log --- res/controllers/common-hid-packet-parser.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 85f30bb4f55..568a31576a4 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -2,13 +2,14 @@ /** * 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 */ - var HIDDebug = function(message) { - print("HID " + message); + console.log("HID " + message); }; + /** * Callback function to call when, the packet represents an HID InputReport, and new data for this 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. From a7dca9e88543e4ac039d458b3c48087e026aaa9f Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 9 Jul 2022 18:23:08 +0200 Subject: [PATCH 11/60] Changed implementation of getOffset, to constant time solution using Math.clz32(), proposed by Niko --- res/controllers/common-hid-packet-parser.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 568a31576a4..c34e3609d1d 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -108,12 +108,11 @@ class HIDBitVector { * @returns {number} Index of the least significant bit that is 1 in `bitmask` */ getOffset(bitmask) { - for (let i = 0; i < 32; i++) { - if ((bitmask >> i) & 1) { - return i; - } - } - return 0; + bitmask >>>= 0 // ensures coercion to Uint32 + // The previous implementation should have returned 32 for an empty bitmask, instead it returned 0 + if (bitmask === 0) return 0; // skipping this step would make it return -1 + bitmask &= -bitmask; // equivalent to `bitmask = bitmask & (~bitmask + 1)` + return 31 - Math.clz32(bitmask); } /** * Add a control bitmask to the HIDBitVector From 244a0af821f0b85bcdb8f7d470e5f90ee234b91a Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 9 Jul 2022 19:14:14 +0200 Subject: [PATCH 12/60] Simplify implementation of constructor of HIDPacket --- res/controllers/common-hid-packet-parser.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index c34e3609d1d..ef1338d895a 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -247,7 +247,7 @@ this.HIDModifierList = HIDModifierList; * Each HIDPacket must be registered to HIDController. * * @param {string} name Name of packet (it makes sense to refer the HID report type and HID Report-ID here e.g. 'InputReport_0x02' or 'OutputReport_0x81') - * @param {number} reportId ReportID of the packet. If the device does not use ReportIDs this must be 0. + * @param {number} reportId ReportID of the packet. If the device does not use ReportIDs this must be 0. [default = 0] * @param {packetCallback} callback function to call when the packet type represents an InputReport an a new report is received. If packet callback is set, the * packet is not parsed by delta functions. * callback is not meaningful for output packets @@ -256,16 +256,11 @@ this.HIDModifierList = HIDModifierList; * the reportId parameter instead. */ class HIDPacket { - constructor(name, reportId, callback, header) { + constructor(name, reportId = 0, callback, header) { this.name = name; this.header = header; this.callback = callback; - - this.reportId = 0; - if (reportId !== undefined) { - this.reportId = reportId; - } - + this.reportId = reportId; this.groups = {}; // Size of various 'pack' values in bytes From d39f9ab1c7ada775542c608a80ff3b6f49a76b61 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 9 Jul 2022 20:54:33 +0200 Subject: [PATCH 13/60] Added todo hint, that the implementation of getOutputField has a performance problem --- res/controllers/common-hid-packet-parser.js | 1 + 1 file changed, 1 insertion(+) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index ef1338d895a..c703e71a851 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1081,6 +1081,7 @@ class HIDController { /** * Find Output control matching give group and name * + * @todo The current implementation of this often called function is very slow anddoes not scale, due to serveral nested loops. * @param {string} m_group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param {string} m_name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) * @returns {bitObject|packetField} Bit or bytewise field - Returns undefined if output field can't be found. From 63000870c4cca8ad7bc1885d61cad32f90c5e901 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 9 Jul 2022 22:04:06 +0200 Subject: [PATCH 14/60] Use const for signed state of signedPackFormats, and unified this across the code --- res/controllers/common-hid-packet-parser.js | 22 +++++++++------------ 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index c703e71a851..dd10b15a497 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -282,11 +282,7 @@ class HIDPacket { return; } const bytes = this.packSizes[field.pack]; - let signed = false; - if (this.signedPackFormats.indexOf(field.pack) !== -1) { - signed = true; - } - + const signed = this.signedPackFormats.includes(field.pack); if (field.type === "bitvector") { // TODO - fix multi byte bit vector outputs if (bytes > 1) { @@ -343,10 +339,7 @@ class HIDPacket { return; } const bytes = this.packSizes[field.pack]; - let signed = false; - if (this.signedPackFormats.indexOf(field.pack) !== -1) { - signed = true; - } + const signed = this.signedPackFormats.includes(field.pack); for (let field_byte = 0; field_byte < bytes; field_byte++) { if (data[field.offset + field_byte] === 255 && field_byte === 4) { @@ -566,7 +559,8 @@ class HIDPacket { field.auto_repeat_interval = undefined; const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); - if (this.signedPackFormats.indexOf(pack) !== -1) { + const signed = this.signedPackFormats.includes(field.pack); + if (signed) { field.min = 0 - (packet_max_value / 2) + 1; field.max = (packet_max_value / 2) - 1; } else { @@ -581,7 +575,8 @@ class HIDPacket { field.mindelta = 0; } else { // bitmask is only defined for fields which are not expected to handle all bits in the control field. For fields with bitmasks, you can define same offset and pack multiple times with different bitmask values to get for example all 8 bits of a buttons state byte to different control fields in addControl input packet command. Masking multiple bits should work but has not been as widely tested. - if (this.signedPackFormats.indexOf(pack) !== -1) { + const signed = this.signedPackFormats.includes(field.pack); + if (signed) { HIDDebug("ERROR registering bitvector: signed fields not supported"); return; } @@ -676,8 +671,9 @@ class HIDPacket { field.callback = callback; field.toggle = undefined; - const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); - if (this.signedPackFormats.indexOf(pack) !== -1) { + const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); + const signed = this.signedPackFormats.includes(field.pack); + if (signed) { field.min = 0 - (packet_max_value / 2) + 1; field.max = (packet_max_value / 2) - 1; } else { From a15910dd16a0ff743c481b15d3ab826015cca31c Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 9 Jul 2022 23:01:20 +0200 Subject: [PATCH 15/60] Replaced variable with a constant --- res/controllers/common-hid-packet-parser.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index dd10b15a497..cfefbd34be7 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -2,7 +2,7 @@ /** * 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 */ @@ -108,9 +108,9 @@ class HIDBitVector { * @returns {number} Index of the least significant bit that is 1 in `bitmask` */ getOffset(bitmask) { - bitmask >>>= 0 // ensures coercion to Uint32 + bitmask >>>= 0; // ensures coercion to Uint32 // The previous implementation should have returned 32 for an empty bitmask, instead it returned 0 - if (bitmask === 0) return 0; // skipping this step would make it return -1 + if (bitmask === 0) { return 0; } // skipping this step would make it return -1 bitmask &= -bitmask; // equivalent to `bitmask = bitmask & (~bitmask + 1)` return 31 - Math.clz32(bitmask); } @@ -276,7 +276,6 @@ class HIDPacket { * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. */ pack(data, field) { - let value; if (!(field.pack in this.packSizes)) { HIDDebug("ERROR parsing packed value: invalid pack format " + field.pack); return; @@ -296,7 +295,7 @@ class HIDPacket { return; } - value = (field.value !== undefined) ? field.value : 0; + const value = (field.value !== undefined) ? field.value : 0; if (value < field.min || value > field.max) { HIDDebug("ERROR " + field.id + " packed value out of range: " + value); @@ -671,7 +670,7 @@ class HIDPacket { field.callback = callback; field.toggle = undefined; - const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); + const packet_max_value = Math.pow(2, this.packSizes[field.pack] * 8); const signed = this.signedPackFormats.includes(field.pack); if (signed) { field.min = 0 - (packet_max_value / 2) + 1; @@ -1077,7 +1076,7 @@ class HIDController { /** * Find Output control matching give group and name * - * @todo The current implementation of this often called function is very slow anddoes not scale, due to serveral nested loops. + * @todo The current implementation of this often called function is very slow anddoes not scale, due to serveral nested loops. * @param {string} m_group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param {string} m_name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) * @returns {bitObject|packetField} Bit or bytewise field - Returns undefined if output field can't be found. From 5905de1d74321691269429eb327c4b5bb2f714bb Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 9 Jul 2022 23:13:04 +0200 Subject: [PATCH 16/60] Codespell --- res/controllers/common-hid-packet-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index cfefbd34be7..5fc69344f94 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1076,7 +1076,7 @@ class HIDController { /** * Find Output control matching give group and name * - * @todo The current implementation of this often called function is very slow anddoes not scale, due to serveral nested loops. + * @todo The current implementation of this often called function is very slow anddoes not scale, due to several nested loops. * @param {string} m_group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. * @param {string} m_name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) * @returns {bitObject|packetField} Bit or bytewise field - Returns undefined if output field can't be found. From c37e3209526bd0bfe1d917412893a6655bc08044 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 10 Jul 2022 12:38:18 +0200 Subject: [PATCH 17/60] Reformated code to break long lines --- res/controllers/common-hid-packet-parser.js | 569 ++++++++++++++------ 1 file changed, 404 insertions(+), 165 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 5fc69344f94..9bd94c59b0a 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -11,8 +11,10 @@ var HIDDebug = function(message) { }; /** - * Callback function to call when, the packet represents an HID InputReport, and new data for this 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 function to call when, the packet represents an HID InputReport, and new data for this + * 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 @@ -22,14 +24,24 @@ var HIDDebug = function(message) { * Callback function to call when, data for specified filed in the packet is updated. * * @callback controlCallback - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + * @param {packetField} field Object that describes a field inside of a packet, which can often + * mapped to a Mixxx control. */ /** - * In almost every case, a HID controller sends data values with input fields which are not directly suitable for Mixxx control values. To solve this issue, HIDController contains function to scale the input value to suitable range automatically before calling any field processing functions. Scalers can be registered with HIDController.registerScalingFunction(group,name,callback) in HIDController. + * In almost every case, a HID controller sends data values with input fields which are not directly + * suitable for Mixxx control values. To solve this issue, HIDController contains function to scale + * the input value to suitable range automatically before calling any field processing functions. + * Scalers can be registered with HIDController.registerScalingFunction(group,name,callback) in + * HIDController. * * @callback scalingCallback - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but if + * it matches a valid Mixxx control group name, it is possible to map a field to a control or + * output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, but if + * it matches a valid Mixxx control name in the group defined for field, the system attempts to + * attach it directly to the correct field. Together group and name form the ID of the field + * (group.name) * @param {number} value Value to be scaled * @returns {number} Scaled value */ @@ -104,22 +116,34 @@ class HIDBitVector { /** * 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. + * @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` */ getOffset(bitmask) { - bitmask >>>= 0; // ensures coercion to Uint32 - // The previous implementation should have returned 32 for an empty bitmask, instead it returned 0 - if (bitmask === 0) { return 0; } // skipping this step would make it return -1 - bitmask &= -bitmask; // equivalent to `bitmask = bitmask & (~bitmask + 1)` + bitmask >>>= 0; // ensures coercion to Uint32 + // The previous implementation should have returned 32 for an empty bitmask, instead it + // returned 0 + if (bitmask === 0) { + return 0; + } // skipping this step would make it return -1 + bitmask &= -bitmask; // equivalent to `bitmask = bitmask & (~bitmask + 1)` return 31 - Math.clz32(bitmask); } /** * Add a control bitmask to the HIDBitVector * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control without any additional code. Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control without any additional code. Defines the group name for the field. The group can + * be any string, but if it matches a valid Mixxx control group name, it is possible to map + * a field to a control or output without any additional code + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) + * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are + * considered. */ addBitMask(group, name, bitmask) { /** @type {bitObject} */ @@ -142,9 +166,15 @@ class HIDBitVector { /** * Add an output control bitmask to the HIDBitVector * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to an output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to an + * output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) + * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are + * considered. */ addOutputMask(group, name, bitmask) { /** @type {bitObject} */ @@ -242,13 +272,17 @@ this.HIDModifierList = HIDModifierList; /** * HID Packet object * - * An HIDPacket represents one HID report of type InputReport or OutputReport (FeatureReports are currently not supported) + * An HIDPacket represents one HID report of type InputReport or OutputReport (FeatureReports are + * currently not supported) * * Each HIDPacket must be registered to HIDController. * - * @param {string} name Name of packet (it makes sense to refer the HID report type and HID Report-ID here e.g. 'InputReport_0x02' or 'OutputReport_0x81') - * @param {number} reportId ReportID of the packet. If the device does not use ReportIDs this must be 0. [default = 0] - * @param {packetCallback} callback function to call when the packet type represents an InputReport an a new report is received. If packet callback is set, the + * @param {string} name Name of packet (it makes sense to refer the HID report type and HID + * Report-ID here e.g. 'InputReport_0x02' or 'OutputReport_0x81') + * @param {number} reportId ReportID of the packet. If the device does not use ReportIDs this must + * be 0. [default = 0] + * @param {packetCallback} callback function to call when the packet type represents an InputReport + * an a new report is received. If packet callback is set, the * packet is not parsed by delta functions. * callback is not meaningful for output packets * @param {number[]} header (optional) list of bytes to match from beginning @@ -273,7 +307,8 @@ class HIDPacket { * Can only pack bits and byte values, patches welcome. * * @param {number[]} 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. + * @param {packetField} field Object that describes a field inside of a packet, which can often + * mapped to a Mixxx control. */ pack(data, field) { if (!(field.pack in this.packSizes)) { @@ -314,7 +349,6 @@ class HIDPacket { data[index] = (value >> (byte_index * 8)) & 255; } } - } /** * Parse and return field value matching the 'pack' field from field attributes. @@ -327,7 +361,8 @@ class HIDPacket { * - I unsigned integer * * @param {number[]} 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. + * @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 */ unpack(data, field) { @@ -362,7 +397,8 @@ class HIDPacket { * * @param {string} name Name of the group * @param {boolean} [create=false] If true, group will be created - @returns {any} Group Returns group or undefined, when group is not existing and create is set to false + @returns {any} Group Returns group or undefined, when group is not existing and create is set + to false */ getGroup(name, create) { if (this.groups === undefined) { @@ -381,8 +417,10 @@ 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 - * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 + * - For HID devices which don't use ReportIDs, the data bytes starts at + * position 0 + * - For HID devices which use ReportIDs to enumerate the reports, the + * data bytes starts at position 1 * @param {object} pack Is one of the field packing types: * - b signed byte * - B unsigned byte @@ -405,14 +443,22 @@ class HIDPacket { for (const field_id in group) { field = group[field_id]; // Same field offset - if (field.offset === offset) { return field; } + if (field.offset === offset) { + return field; + } // 7-8 8-9 // Offset for smaller packet inside multibyte field - if (field.offset < offset && field.end_offset >= end_offset) { return field; } + if (field.offset < offset && field.end_offset >= end_offset) { + return field; + } // Packet offset starts inside field, may overflow - if (field.offset < offset && field.end_offset > offset) { return field; } + if (field.offset < offset && field.end_offset > offset) { + return field; + } // Packet start before field, ends or overflows field - if (field.offset > offset && field.offset < end_offset) { return field; } + if (field.offset > offset && field.offset < end_offset) { + return field; + } } } return undefined; @@ -421,8 +467,13 @@ class HIDPacket { * Return a field by group and name from the packet, * Returns undefined if field could not be found * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @returns {packetField} Field */ getField(group, name) { @@ -433,7 +484,9 @@ class HIDPacket { } let control_group = this.groups[group]; - if (field_id in control_group) { return control_group[field_id]; } + if (field_id in control_group) { + return control_group[field_id]; + } // Lookup for bit fields in bitvector matching field name for (const group_name in this.groups) { @@ -457,8 +510,13 @@ class HIDPacket { /** * Return reference to a bit in a bitvector field * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @returns {packetField} Reference to a bit in a bitvector field */ lookupBit(group, name) { @@ -480,8 +538,13 @@ class HIDPacket { /** * Remove a control registered. Normally not needed * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) */ removeControl(group, name) { const control_group = this.getGroup(group); @@ -494,16 +557,25 @@ class HIDPacket { /** * Register a numeric value to parse from input packet * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. control group name - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. control group name + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @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 - * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 + * - For HID devices which don't use ReportIDs, the data bytes starts at + * position 0 + * - For HID devices which use ReportIDs to enumerate the reports, the + * data bytes starts at position 1 * @param pack control packing format for unpack(), one of b/B, h/H, i/I - * @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 reported + * @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 + * reported * @param {controlCallback} callback Callback function for the control */ addControl(group, name, offset, pack, bitmask, isEncoder, callback) { @@ -522,16 +594,16 @@ class HIDPacket { if (field !== undefined) { if (bitmask === undefined) { HIDDebug("ERROR registering offset " + offset + " pack " + pack); - HIDDebug( - "ERROR trying to overwrite non-bitmask control " + group + " " + name - ); + HIDDebug("ERROR trying to overwrite non-bitmask control " + group + " " + name); return; } bitvector = field.value; bitvector.addBitMask(group, name, bitmask); if (callback !== undefined) { if (typeof callback !== "function") { - HIDDebug("ERROR callback provided for " + group + "." + name + " is not a function."); + HIDDebug( + "ERROR callback provided for " + group + "." + name + + " is not a function."); return; } this.setCallback(group, name, callback); @@ -573,7 +645,11 @@ class HIDPacket { field.delta = 0; field.mindelta = 0; } else { - // bitmask is only defined for fields which are not expected to handle all bits in the control field. For fields with bitmasks, you can define same offset and pack multiple times with different bitmask values to get for example all 8 bits of a buttons state byte to different control fields in addControl input packet command. Masking multiple bits should work but has not been as widely tested. + // bitmask is only defined for fields which are not expected to handle all bits in the + // control field. For fields with bitmasks, you can define same offset and pack multiple + // times with different bitmask values to get for example all 8 bits of a buttons state + // byte to different control fields in addControl input packet command. Masking multiple + // bits should work but has not been as widely tested. const signed = this.signedPackFormats.includes(field.pack); if (signed) { HIDDebug("ERROR registering bitvector: signed fields not supported"); @@ -599,7 +675,8 @@ class HIDPacket { if (callback !== undefined) { if (typeof callback !== "function") { - HIDDebug("ERROR callback provided for " + group + "." + name + " is not a function."); + HIDDebug( + "ERROR callback provided for " + group + "." + name + " is not a function."); return; } this.setCallback(group, name, callback); @@ -615,13 +692,21 @@ class HIDPacket { * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @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 - * - For HID devices which use ReportIDs to enumerate the reports, the data bytes starts at position 1 + * - For HID devices which don't use ReportIDs, the data bytes starts at + * position 0 + * - For HID devices which use ReportIDs to enumerate the reports, the + * data bytes starts at position 1 * @param pack control packing format for pack(), one of b/B, h/H, i/I - * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are considered. + * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are + * considered. * @param {controlCallback} [callback=undefined] Callback function for the control */ addOutput(group, name, offset, pack, bitmask, callback) { @@ -647,9 +732,7 @@ class HIDPacket { field = this.getFieldByOffset(offset, pack); if (field !== undefined) { if (bitmask === undefined) { - HIDDebug( - "ERROR: overwrite non-bitmask control " + group + "." + name - ); + HIDDebug("ERROR: overwrite non-bitmask control " + group + "." + name); return; } bitvector = field.value; @@ -706,8 +789,13 @@ 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param {controlCallback} callback Callback function for the control */ setCallback(group, name, callback) { @@ -718,8 +806,7 @@ class HIDPacket { return; } if (field === undefined) { - HIDDebug("setCallback: field for " + field_id + " not found" - ); + HIDDebug("setCallback: field for " + field_id + " not found"); return; } if (field.type === "bitvector") { @@ -737,11 +824,17 @@ 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. + * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param {boolean} ignored 'ignored' flag for field to given value (true or false) */ setIgnored(group, name, ignored) { @@ -756,8 +849,13 @@ class HIDPacket { * Adjust field's minimum delta value. * Input value changes smaller than this are not reported in delta * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param {number} mindelta Minimum delta value. */ setMinDelta(group, name, mindelta) { @@ -775,7 +873,8 @@ 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 {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. * @returns {HIDBitVector} List of modified bits (delta) */ @@ -825,7 +924,9 @@ class HIDPacket { if (field.type === "bitvector") { // Bitvector deltas are checked in parseBitVector const changed_bits = this.parseBitVector(field, value); - for (const bit_name in changed_bits) { field_changes[bit_name] = changed_bits[bit_name]; } + for (const bit_name in changed_bits) { + field_changes[bit_name] = changed_bits[bit_name]; + } } else if (field.type === "control") { if (field.value === value && field.mindelta !== undefined) { @@ -937,7 +1038,8 @@ this.HIDPacket = HIDPacket; * @property {boolean} rampedScratchEnable * @property {boolean} rampedScratchDisable * @property {any} enableScratchCallback - * @property {number} auto_repeat_interval Auto repeat interval default for fields, where not specified individual + * @property {number} auto_repeat_interval Auto repeat interval default for fields, where not + * specified individual */ class HIDController { constructor() { @@ -969,9 +1071,11 @@ class HIDController { // Output color values to send this.LEDColors = {off: 0x0, on: 0x7f}; // Toggle buttons - this.toggleButtons = ["play", "pfl", "keylock", "quantize", "reverse", "slip_enabled", + this.toggleButtons = [ + "play", "pfl", "keylock", "quantize", "reverse", "slip_enabled", "group_[Channel1]_enable", "group_[Channel2]_enable", - "group_[Channel3]_enable", "group_[Channel4]_enable"]; + "group_[Channel3]_enable", "group_[Channel4]_enable" + ]; // Override to set specific colors for multicolor button Output per deck this.deckOutputColors = {1: "on", 2: "on", 3: "on", 4: "on"}; @@ -983,12 +1087,28 @@ class HIDController { // HID packet parser to recognize group parameters we should // try sending to mixxx. this.valid_groups = [ - "[Channel1]", "[Channel2]", "[Channel3]", "[Channel4]", - "[Sampler1]", "[Sampler2]", "[Sampler3]", "[Sampler4]", - "[Sampler5]", "[Sampler6]", "[Sampler7]", "[Sampler8]", - "[Master]", "[PreviewDeck1]", "[Effects]", "[Playlist]", "[Flanger]", - "[Microphone]", "[EffectRack1_EffectUnit1]", "[EffectRack1_EffectUnit2]", - "[EffectRack1_EffectUnit3]", "[EffectRack1_EffectUnit4]", + "[Channel1]", + "[Channel2]", + "[Channel3]", + "[Channel4]", + "[Sampler1]", + "[Sampler2]", + "[Sampler3]", + "[Sampler4]", + "[Sampler5]", + "[Sampler6]", + "[Sampler7]", + "[Sampler8]", + "[Master]", + "[PreviewDeck1]", + "[Effects]", + "[Playlist]", + "[Flanger]", + "[Microphone]", + "[EffectRack1_EffectUnit1]", + "[EffectRack1_EffectUnit2]", + "[EffectRack1_EffectUnit3]", + "[EffectRack1_EffectUnit4]", "[InternalClock]" ]; @@ -1016,19 +1136,24 @@ class HIDController { * Initialize our packet data and callbacks. This does not seem to * work when executed from here, but we keep a stub just in case. */ - initializePacketData() { - } + initializePacketData() {} /** * Return deck number from deck name. Deck name can't be virtual deck name * in this function call. * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. * @returns {number} Number of deck */ resolveDeck(group) { - if (group === undefined) { return undefined; } + if (group === undefined) { + return undefined; + } const result = group.match(/\[Channel[0-9]+\]/); - if (!result) { return undefined; } + if (!result) { + return undefined; + } const str = group.replace(/\[Channel/, ""); return str.substring(0, str.length - 1); } @@ -1039,14 +1164,18 @@ class HIDController { * @returns {string} Group name of the deck (e.g. Channel2 for deck number 2) */ resolveDeckGroup(deck) { - if (deck === undefined) { return undefined; } + if (deck === undefined) { + return undefined; + } return "[Channel" + deck + "]"; } /** * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. * @returns {string} Channel */ resolveGroup(group) { @@ -1076,10 +1205,17 @@ class HIDController { /** * Find Output control matching give group and name * - * @todo The current implementation of this often called function is very slow anddoes not scale, due to several nested loops. - * @param {string} m_group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} m_name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) - * @returns {bitObject|packetField} Bit or bytewise field - Returns undefined if output field can't be found. + * @todo The current implementation of this often called function is very slow anddoes not + * scale, due to several nested loops. + * @param {string} m_group Defines the group name for the field. The group can be any string, + * but if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} m_name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) + * @returns {bitObject|packetField} Bit or bytewise field - Returns undefined if output field + * can't be found. */ getOutputField(m_group, m_name) { for (const packet_name in this.OutputPackets) { @@ -1091,13 +1227,21 @@ class HIDController { if (field.type === "bitvector") { for (const bit_id in field.value.bits) { const bit = field.value.bits[bit_id]; - if (bit.mapped_group === m_group && bit.mapped_name === m_name) { return bit; } - if (bit.group === m_group && bit.name === m_name) { return bit; } + if (bit.mapped_group === m_group && bit.mapped_name === m_name) { + return bit; + } + if (bit.group === m_group && bit.name === m_name) { + return bit; + } } continue; } - if (field.mapped_group === m_group && field.mapped_name === m_name) { return field; } - if (field.group === m_group && field.name === m_name) { return field; } + if (field.mapped_group === m_group && field.mapped_name === m_name) { + return field; + } + if (field.group === m_group && field.name === m_name) { + return field; + } } } } @@ -1107,7 +1251,8 @@ 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') + * @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 */ getInputPacket(name) { @@ -1120,7 +1265,8 @@ 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') + * @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 */ getOutputPacket(name) { @@ -1145,8 +1291,13 @@ class HIDController { * 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 Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param {controlCallback} callback Callback function for the control */ setCallback(packet, group, name, callback) { @@ -1175,7 +1326,8 @@ class HIDController { * * @param {string} name Reference of the scaling function in scalers list of HIDController * @param _callback Unused - * @returns {scalingCallback} Scaling function. Returns undefined if function is not registered. + * @returns {scalingCallback} Scaling function. Returns undefined if function is not + * registered. */ getScaler(name, _callback) { if (!(name in this.scalers)) { @@ -1186,16 +1338,19 @@ class HIDController { /** * Change type of a previously defined field to modifier and register it * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param {any} modifier */ linkModifier(group, name, modifier) { const packet = this.getInputPacket(this.defaultPacket); if (packet === undefined) { - HIDDebug( - "ERROR creating modifier: input packet " + this.defaultPacket + " not found" - ); + HIDDebug("ERROR creating modifier: input packet " + this.defaultPacket + " not found"); return; } const bit_id = group + "." + name; @@ -1221,8 +1376,13 @@ class HIDController { /** * Link a previously declared HID control to actual mixxx control * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param {string} m_group Mapped group * @param {string} m_name Mapped name * @param {controlCallback} callback Callback function for the control @@ -1255,11 +1415,15 @@ class HIDController { /** * TODO - implement unlinking of controls * - * @param {string} _group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} _name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} _group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} _name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) */ - unlinkControl(_group, _name) { - } + unlinkControl(_group, _name) {} /** * Register HID input packet type to controller. * Input packets can be responses from device to queries, or control @@ -1279,10 +1443,14 @@ class HIDController { for (const bit_id in field.value.bits) { const bit = field.value.bits[bit_id]; bit.packet = packet; - if (bit.group === "modifiers") { this.modifiers.add(bit.name); } + if (bit.group === "modifiers") { + this.modifiers.add(bit.name); + } } } else { - if (field.group === "modifiers") { this.modifiers.add(field.name); } + if (field.group === "modifiers") { + this.modifiers.add(field.name); + } } } } @@ -1322,7 +1490,8 @@ class HIDController { * - If defined, calls processDelta for results after processing automated fields * * @param {number[]} 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 + * 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 */ parsePacket(data, length) { @@ -1351,7 +1520,9 @@ class HIDController { break; } } - if (packet === undefined) { continue; } + if (packet === undefined) { + continue; + } } changed_data = packet.parse(data); if (packet.callback !== undefined) { @@ -1359,14 +1530,22 @@ class HIDController { return; } // Process named group controls - if (packet.name === this.defaultPacket) { this.processIncomingPacket(packet, changed_data); } + if (packet.name === this.defaultPacket) { + this.processIncomingPacket(packet, changed_data); + } // Process generic changed_data packet, if callback is defined - if (this.processDelta !== undefined) { this.processDelta(packet, changed_data); } - if (this.postProcessDelta !== undefined) { this.postProcessDelta(packet, changed_data); } + if (this.processDelta !== undefined) { + this.processDelta(packet, changed_data); + } + if (this.postProcessDelta !== undefined) { + this.postProcessDelta(packet, changed_data); + } return; } HIDDebug("Received unknown packet of " + length + " bytes"); - for (const i in data) { HIDDebug("BYTE " + data[i]); } + for (const i in data) { + HIDDebug("BYTE " + data[i]); + } } /** * Process the modified field values (delta) from input packet fields for @@ -1413,7 +1592,8 @@ class HIDController { /** * Get active group for this field * - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + * @param {packetField} field Object that describes a field inside of a packet, which can often + * mapped to a Mixxx control. * @returns {string} Group */ getActiveFieldGroup(field) { @@ -1427,7 +1607,7 @@ class HIDController { } } if (this.valid_groups.indexOf(group) !== -1) { - //HIDDebug("Resolving group " + group); + // HIDDebug("Resolving group " + group); return this.resolveGroup(group); } return group; @@ -1435,7 +1615,8 @@ class HIDController { /** * Get active control name from field * - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + * @param {packetField} field Object that describes a field inside of a packet, which can often + * mapped to a Mixxx control. * @returns {string} Name of field */ getActiveFieldControl(field) { @@ -1448,17 +1629,17 @@ class HIDController { /** * Process given button field, triggering events * - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + * @param {packetField} field Object that describes a field inside of a packet, which can often + * mapped to a Mixxx control. */ processButton(field) { const group = this.getActiveFieldGroup(field); const control = this.getActiveFieldControl(field); if (group === undefined) { - HIDDebug("processButton: Could not resolve group from " - + field.group + " " + field.mapped_group + " " - + field.name + " " + field.mapped_name - ); + HIDDebug( + "processButton: Could not resolve group from " + field.group + " " + + field.mapped_group + " " + field.name + " " + field.mapped_name); return; } @@ -1493,7 +1674,9 @@ class HIDController { return; } if (this.toggleButtons.indexOf(control) !== -1) { - if (field.value === this.buttonStates.released) { return; } + if (field.value === this.buttonStates.released) { + return; + } if (engine.getValue(group, control)) { if (control === "play") { engine.setValue(group, "stop", true); @@ -1517,7 +1700,8 @@ 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. + * @param {packetField} field Object that describes a field inside of a packet, which can often + * mapped to a Mixxx control. */ processControl(field) { let value; @@ -1525,10 +1709,9 @@ class HIDController { const control = this.getActiveFieldControl(field); if (group === undefined) { - HIDDebug("processControl: Could not resolve group from " - + field.group + " " + field.mapped_group + " " - + field.name + " " + field.mapped_name - ); + HIDDebug( + "processControl: Could not resolve group from " + field.group + " " + + field.mapped_group + " " + field.name + " " + field.mapped_name); return; } @@ -1571,7 +1754,9 @@ class HIDController { /** * Toggle control state from toggle button * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. * @param control * @param value */ @@ -1585,8 +1770,11 @@ class HIDController { /** * Toggle play/pause state * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {packetField} field Object that describes a field inside of a packet, which can often mapped to a Mixxx control. + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {packetField} field Object that describes a field inside of a packet, which can often + * mapped to a Mixxx control. */ togglePlay(group, field) { if (field.value === this.buttonStates.released) { @@ -1604,7 +1792,9 @@ class HIDController { * when scratching should be enabled. * Deck is resolved from group with 'resolveDeck' * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. * @param {boolean} status Enable or Disable scratching: * - true enables scratching (press 'jog_touch' button) * Sets the internal 'isScratchEnabled' attribute to true, and calls scratchEnable @@ -1625,11 +1815,15 @@ class HIDController { this.scratchBeta, this.rampedScratchEnable ); - if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(true); } + if (this.enableScratchCallback !== undefined) { + this.enableScratchCallback(true); + } } else { this.isScratchEnabled = false; engine.scratchDisable(deck, this.rampedScratchDisable); - if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(false); } + if (this.enableScratchCallback !== undefined) { + this.enableScratchCallback(false); + } } } /** @@ -1649,16 +1843,23 @@ class HIDController { * '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. + * @param {packetField} field Object that describes a field inside of a packet, which can often + * mapped to a Mixxx control. */ jog_wheel(field) { let scaler = undefined; const active_group = this.getActiveFieldGroup(field); let value = undefined; - if (field.isEncoder) { value = field.delta; } else { value = field.value; } + if (field.isEncoder) { + value = field.delta; + } else { + value = field.value; + } if (this.isScratchEnabled) { const deck = this.resolveDeck(active_group); - if (deck === undefined) { return; } + if (deck === undefined) { + return; + } scaler = this.getScaler("jog_scratch"); if (scaler !== undefined) { value = scaler(active_group, "jog_scratch", value); @@ -1667,7 +1868,9 @@ class HIDController { } engine.scratchTick(deck, value); } else { - if (active_group === undefined) { return; } + if (active_group === undefined) { + return; + } scaler = this.getScaler("jog"); if (scaler !== undefined) { value = scaler(active_group, "jog", value); @@ -1687,7 +1890,7 @@ class HIDController { engine.stopTimer(this.timers[timer_id]); delete this.timers[timer_id]; } else { - //HIDDebug("No such autorepeat timer: " + timer_id); + // HIDDebug("No such autorepeat timer: " + timer_id); } } /** @@ -1711,7 +1914,9 @@ class HIDController { } else { field.auto_repeat_interval = controller.auto_repeat_interval; } - if (callback) { callback(field); } + if (callback) { + callback(field); + } } /** * Callback for auto repeat timer to send again the values for @@ -1732,12 +1937,16 @@ class HIDController { for (field_name in group) { field = group[field_name]; if (field.type !== "bitvector") { - if (field.auto_repeat) { this.processControl(field); } + if (field.auto_repeat) { + this.processControl(field); + } continue; } for (bit_name in field.value.bits) { bit = field.value.bits[bit_name]; - if (bit.auto_repeat) { this.processButton(bit); } + if (bit.auto_repeat) { + this.processButton(bit); + } } } } @@ -1759,12 +1968,16 @@ class HIDController { // var totalDecks = engine.getValue("[Master]","num_decks"); // deck = (this.activeDeck+1) % totalDecks; deck = this.deckSwitchMap[this.activeDeck]; - if (deck === undefined) { deck = 1; } + if (deck === undefined) { + deck = 1; + } } } const new_group = this.resolveDeckGroup(deck); HIDDebug("Switching to deck " + deck + " group " + new_group); - if (this.disconnectDeck !== undefined) { this.disconnectDeck(); } + if (this.disconnectDeck !== undefined) { + this.disconnectDeck(); + } for (const packet_name in this.OutputPackets) { packet = this.OutputPackets[packet_name]; for (const group_name in packet.groups) { @@ -1774,17 +1987,19 @@ class HIDController { if (field.type === "bitvector") { for (const bit_id in field.value.bits) { const bit = field.value.bits[bit_id]; - if (this.virtualDecks.indexOf(bit.mapped_group) === -1) { continue; } + if (this.virtualDecks.indexOf(bit.mapped_group) === -1) { + continue; + } controlgroup = this.resolveGroup(bit.mapped_group); - engine.connectControl(controlgroup, bit.mapped_name, bit.mapped_callback, true); + engine.connectControl( + controlgroup, bit.mapped_name, bit.mapped_callback, true); engine.connectControl(new_group, bit.mapped_name, bit.mapped_callback); var value = engine.getValue(new_group, bit.mapped_name); HIDDebug("BIT " + bit.group + "." + bit.name + " value " + value); if (value) { this.setOutput( bit.group, bit.name, - this.LEDColors[this.deckOutputColors[deck]] - ); + this.LEDColors[this.deckOutputColors[deck]]); } else { this.setOutput( bit.group, bit.name, @@ -1795,9 +2010,12 @@ class HIDController { continue; } // Only move outputs of virtual decks - if (this.virtualDecks.indexOf(field.mapped_group) === -1) { continue; } + if (this.virtualDecks.indexOf(field.mapped_group) === -1) { + continue; + } controlgroup = this.resolveGroup(field.mapped_group); - engine.connectControl(controlgroup, field.mapped_name, field.mapped_callback, true); + engine.connectControl( + controlgroup, field.mapped_name, field.mapped_callback, true); engine.connectControl(new_group, field.mapped_name, field.mapped_callback); value = engine.getValue(new_group, field.mapped_name); if (value) { @@ -1822,8 +2040,13 @@ class HIDController { /** * Link a virtual HID Output to mixxx control * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param {string} m_group Mapped group * @param {string} m_name Name of mapped control * @param {controlCallback} callback Callback function for the control @@ -1853,8 +2076,13 @@ class HIDController { /** * Unlink a virtual HID Output from mixxx control * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param {controlCallback} callback Callback function for the control */ unlinkOutput(group, name, callback) { @@ -1877,10 +2105,16 @@ class HIDController { /** * Set output state to given value * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param {number} 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 immediately + * @param {boolean} [send_packet=false] If true, the packet (an HID OutputReport) is send + * immediately */ setOutput(group, name, value, send_packet) { const field = this.getOutputField(group, name); @@ -1897,8 +2131,13 @@ class HIDController { /** * Set Output to toggle between two values. Reset with setOutput(name,'off') * - * @param {string} group Defines the group name for the field. The group can be any string, but if it matches a valid Mixxx control group name, it is possible to map a field to a control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if it matches a valid Mixxx control name in the group defined for field, the system attempts to attach it directly to the correct field. Together group and name form the ID of the field (group.name) + * @param {string} group Defines the group name for the field. The group can be any string, but + * if it matches a valid Mixxx control group name, it is possible to map a field to a + * control or output without any additional code. + * @param {string} name Is the name of the control for the field. The name can be any string, + * but if it matches a valid Mixxx control name in the group defined for field, the system + * attempts to attach it directly to the correct field. Together group and name form the ID + * of the field (group.name) * @param toggle_value */ setOutputToggle(group, name, toggle_value) { From 5dad81cae2d5f8b5c369a9a3ff4109dd98e07274 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 23 Jul 2022 14:56:15 +0200 Subject: [PATCH 18/60] Replaced usage of deprecated HIDDebug by console.log, console.warn or console.error depending on the context Instead of the unnecessary prefix HID, it prints the function name. Errors and Warnings are marked in the console output anyway, therefore I removed the texts "ERROR" and "WARNING" where they were used. --- res/controllers/common-hid-packet-parser.js | 127 ++++++++++---------- 1 file changed, 65 insertions(+), 62 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 9bd94c59b0a..5d8facdbcf1 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -216,7 +216,7 @@ class HIDModifierList { */ add(name) { if (name in this.modifiers) { - HIDDebug("Modifier already defined: " + name); + console.warn("HIDModifierList.add - Modifier already defined: " + name); return; } this.modifiers[name] = undefined; @@ -229,7 +229,7 @@ class HIDModifierList { */ set(name, value) { if (!(name in this.modifiers)) { - HIDDebug("Unknown modifier: " + name); + console.error("HIDModifierList.set - Unknown modifier: " + name); return; } this.modifiers[name] = value; @@ -246,7 +246,7 @@ class HIDModifierList { */ get(name) { if (!(name in this.modifiers)) { - HIDDebug("Unknown modifier: " + name); + console.error("HIDModifierList.get - Unknown modifier: " + name); return false; } return this.modifiers[name]; @@ -259,7 +259,7 @@ class HIDModifierList { */ setCallback(name, callback) { if (!(name in this.modifiers)) { - HIDDebug("Unknown modifier: " + name); + console.error("HIDModifierList.setCallback - Unknown modifier: " + name); return; } this.callbacks[name] = callback; @@ -312,7 +312,7 @@ class HIDPacket { */ pack(data, field) { if (!(field.pack in this.packSizes)) { - HIDDebug("ERROR parsing packed value: invalid pack format " + field.pack); + console.error("HIDPacket.pack - Parsing packed value: invalid pack format " + field.pack); return; } const bytes = this.packSizes[field.pack]; @@ -320,7 +320,7 @@ class HIDPacket { if (field.type === "bitvector") { // TODO - fix multi byte bit vector outputs if (bytes > 1) { - HIDDebug("ERROR: packing multibyte bit vectors not yet supported"); + console.error("HIDPacket.pack - Packing multibyte bit vectors not yet supported"); return; } for (const bit_id in field.value.bits) { @@ -333,7 +333,7 @@ class HIDPacket { const value = (field.value !== undefined) ? field.value : 0; if (value < field.min || value > field.max) { - HIDDebug("ERROR " + field.id + " packed value out of range: " + value); + console.error("HIDPacket.pack - " + field.id + " packed value out of range: " + value); return; } @@ -369,7 +369,7 @@ class HIDPacket { let value = 0; if (!(field.pack in this.packSizes)) { - HIDDebug("ERROR parsing packed value: invalid pack format " + field.pack); + console.error("HIDPacket.unpack - Parsing packed value: invalid pack format " + field.pack); return; } const bytes = this.packSizes[field.pack]; @@ -432,7 +432,7 @@ class HIDPacket { */ getFieldByOffset(offset, pack) { if (!(pack in this.packSizes)) { - HIDDebug("Unknown pack string " + pack); + console.error("HIDPacket.getFieldByOffset - Unknown pack string " + pack); return undefined; } const end_offset = offset + this.packSizes[pack]; @@ -479,7 +479,7 @@ class HIDPacket { getField(group, name) { const field_id = group + "." + name; if (!(group in this.groups)) { - HIDDebug("PACKET " + this.name + " group not found " + group); + console.error("HIDPacket.getField - Packet " + this.name + " group not found " + group); return undefined; } @@ -522,7 +522,7 @@ class HIDPacket { lookupBit(group, name) { const field = this.getField(group, name); if (field === undefined) { - HIDDebug("Bitvector match not found: " + group + "." + name); + console.error("HIDPacket.lookupBit - Bitvector match not found: " + group + "." + name); return undefined; } const bit_id = group + "." + name; @@ -532,7 +532,7 @@ class HIDPacket { return bit; } } - HIDDebug("BUG: bit not found after successful field lookup"); + console.error("HIDPacket.lookupBit - BUG: bit not found after successful field lookup"); return undefined; } /** @@ -549,7 +549,7 @@ class HIDPacket { removeControl(group, name) { const control_group = this.getGroup(group); if (!(name in control_group)) { - HIDDebug("Field not in control group " + group + ": " + name); + console.warn("HIDPacket.removeControl - Field not in control group " + group + ": " + name); return; } delete control_group[name]; @@ -582,27 +582,27 @@ class HIDPacket { const control_group = this.getGroup(group, true); let bitvector = undefined; if (control_group === undefined) { - HIDDebug("ERROR creating HID packet group " + group); + console.error("HIDPacket.addControl - Creating HID packet group " + group); return; } if (!(pack in this.packSizes)) { - HIDDebug("Unknown pack value " + pack); + console.error("HIDPacket.addControl - Unknown pack value " + pack); return; } let field = this.getFieldByOffset(offset, pack); if (field !== undefined) { if (bitmask === undefined) { - HIDDebug("ERROR registering offset " + offset + " pack " + pack); - HIDDebug("ERROR trying to overwrite non-bitmask control " + group + " " + name); + console.error("HIDPacket.addControl - Registering offset " + offset + " pack " + pack); + console.error("HIDPacket.addControl - Trying to overwrite non-bitmask control " + group + " " + name); return; } bitvector = field.value; bitvector.addBitMask(group, name, bitmask); if (callback !== undefined) { if (typeof callback !== "function") { - HIDDebug( - "ERROR callback provided for " + group + "." + name + + console.error( + "HIDPacket.addControl - Callback provided for " + group + "." + name + " is not a function."); return; } @@ -652,7 +652,7 @@ class HIDPacket { // bits should work but has not been as widely tested. const signed = this.signedPackFormats.includes(field.pack); if (signed) { - HIDDebug("ERROR registering bitvector: signed fields not supported"); + console.error("HIDPacket.addControl - Registering bitvector: signed fields not supported"); return; } // Create a new bitvector field and add the bit to that @@ -675,8 +675,9 @@ class HIDPacket { if (callback !== undefined) { if (typeof callback !== "function") { - HIDDebug( - "ERROR callback provided for " + group + "." + name + " is not a function."); + console.error( + "HIDPacket.addControl - Callback provided for " + group + "." + name + + " is not a function."); return; } this.setCallback(group, name, callback); @@ -720,7 +721,7 @@ class HIDPacket { return; } if (!(pack in this.packSizes)) { - HIDDebug("ERROR: unknown Output control pack value " + pack); + console.error("HIDPacket.addOutput - Unknown Output control pack value " + pack); return; } @@ -732,7 +733,7 @@ class HIDPacket { field = this.getFieldByOffset(offset, pack); if (field !== undefined) { if (bitmask === undefined) { - HIDDebug("ERROR: overwrite non-bitmask control " + group + "." + name); + console.error("HIDPacket.addOutput - Overwrite non-bitmask control " + group + "." + name); return; } bitvector = field.value; @@ -802,11 +803,11 @@ class HIDPacket { const field = this.getField(group, name); const field_id = group + "." + name; if (callback === undefined) { - HIDDebug("Callback to add was undefined for " + field_id); + console.error("HIDPacket.setCallback - Callback to add was undefined for " + field_id); return; } if (field === undefined) { - HIDDebug("setCallback: field for " + field_id + " not found"); + console.error("HIDPacket.setCallback - Field for " + field_id + " not found"); return; } if (field.type === "bitvector") { @@ -818,7 +819,7 @@ class HIDPacket { bit.callback = callback; return; } - HIDDebug("ERROR: BIT NOT FOUND " + field_id); + console.error("HIDPacket.setCallback - Bit not found " + field_id); } else { field.callback = callback; } @@ -840,7 +841,7 @@ class HIDPacket { setIgnored(group, name, ignored) { const field = this.getField(group, name); if (field === undefined) { - HIDDebug("ERROR setting ignored flag for " + group + " " + name); + console.error("HIDPacket.setIgnored - Setting ignored flag for " + group + " " + name); return; } field.ignored = ignored; @@ -861,11 +862,11 @@ class HIDPacket { setMinDelta(group, name, mindelta) { const field = this.getField(group, name); if (field === undefined) { - HIDDebug("ERROR adjusting mindelta for " + group + " " + name); + console.error("HIDPacket.setMinDelta - Adjusting mindelta for " + group + " " + name); return; } if (field.type === "bitvector") { - HIDDebug("ERROR setting mindelta for bitvector packet does not make sense"); + console.error("HIDPacket.setMinDelta - Setting mindelta for bitvector packet does not make sense"); return; } field.mindelta = mindelta; @@ -917,7 +918,7 @@ class HIDPacket { const value = this.unpack(data, field); if (value === undefined) { - HIDDebug("Error parsing packet field value for " + field_id); + console.error("HIDPacket.parse - Parsing packet field value for " + field_id); return; } @@ -995,7 +996,7 @@ class HIDPacket { } packet_string += data[d].toString(16) + " "; } - HIDDebug("Sending packet with Report ID " + this.reportId + ": " + packet_string); + console.log("Sending packet with Report ID " + this.reportId + ": " + packet_string); } controller.send(data, data.length, this.reportId); } @@ -1303,7 +1304,7 @@ class HIDController { setCallback(packet, group, name, callback) { const input_packet = this.getInputPacket(packet); if (input_packet === undefined) { - HIDDebug("Input packet not found " + packet); + console.error("HIDController.setCallback - Input packet not found " + packet); return; } input_packet.setCallback(group, name, callback); @@ -1350,13 +1351,13 @@ class HIDController { linkModifier(group, name, modifier) { const packet = this.getInputPacket(this.defaultPacket); if (packet === undefined) { - HIDDebug("ERROR creating modifier: input packet " + this.defaultPacket + " not found"); + console.error("HIDController.linkModifier - Creating modifier: input packet " + this.defaultPacket + " not found"); return; } const bit_id = group + "." + name; const field = packet.lookupBit(group, name); if (field === undefined) { - HIDDebug("BIT field not found: " + bit_id); + console.error("HIDController.linkModifier - Bit field not found: " + bit_id); return; } field.group = "modifiers"; @@ -1371,7 +1372,7 @@ class HIDController { * @param _modifier Unused */ unlinkModifier(_group, _name, _modifier) { - HIDDebug("Unlinking of modifiers not yet implemented"); + console.warn("HIDController.unlinkModifier - Unlinking of modifiers not yet implemented"); } /** * Link a previously declared HID control to actual mixxx control @@ -1391,18 +1392,18 @@ class HIDController { let field; const packet = this.getInputPacket(this.defaultPacket); if (packet === undefined) { - HIDDebug("ERROR creating modifier: input packet " + this.defaultPacket + " not found"); + console.error("HIDController.linkControl - Creating modifier: input packet " + this.defaultPacket + " not found"); return; } field = packet.getField(group, name); if (field === undefined) { - HIDDebug("Field not found: " + group + "." + name); + console.error("HIDController.linkControl - Field not found: " + group + "." + name); return; } if (field.type === "bitvector") { field = packet.lookupBit(group, name); if (field === undefined) { - HIDDebug("bit not found: " + group + "." + name); + console.error("HIDController.linkControl - Bit not found: " + group + "." + name); return; } } @@ -1542,9 +1543,9 @@ class HIDController { } return; } - HIDDebug("Received unknown packet of " + length + " bytes"); + console.warn("HIDController.parsePacket - Received unknown packet of " + length + " bytes"); for (const i in data) { - HIDDebug("BYTE " + data[i]); + console.log("BYTE " + data[i]); } } /** @@ -1585,7 +1586,7 @@ class HIDController { // Numeric value field this.processControl(field); } else { - HIDDebug("Unknown field " + field.name + " type " + field.type); + console.warn("HIDController.processIncomingPacket - Unknown field " + field.name + " type " + field.type); } } } @@ -1607,7 +1608,7 @@ class HIDController { } } if (this.valid_groups.indexOf(group) !== -1) { - // HIDDebug("Resolving group " + group); + // console.log("Resolving group " + group); return this.resolveGroup(group); } return group; @@ -1637,9 +1638,10 @@ class HIDController { const control = this.getActiveFieldControl(field); if (group === undefined) { - HIDDebug( - "processButton: Could not resolve group from " + field.group + " " + - field.mapped_group + " " + field.name + " " + field.mapped_name); + console.warn( + "HIDController.processButton - Could not resolve group from " + + field.group + " " + field.mapped_group + " " + + field.name + " " + field.mapped_name); return; } @@ -1689,7 +1691,7 @@ class HIDController { return; } if (field.auto_repeat && field.value === this.buttonStates.pressed) { - HIDDebug("Callback for " + field.group); + console.log("Callback for " + field.group); engine.setValue(group, control, field.auto_repeat(field)); } else if (engine.getValue(group, control) === false) { engine.setValue(group, control, true); @@ -1709,9 +1711,10 @@ class HIDController { const control = this.getActiveFieldControl(field); if (group === undefined) { - HIDDebug( - "processControl: Could not resolve group from " + field.group + " " + - field.mapped_group + " " + field.name + " " + field.mapped_name); + console.warn( + "HIDController.processControl - Could not resolve group from " + + field.group + " " + field.mapped_group + " " + + field.name + " " + field.mapped_name); return; } @@ -1864,7 +1867,7 @@ class HIDController { if (scaler !== undefined) { value = scaler(active_group, "jog_scratch", value); } else { - HIDDebug("WARNING non jog_scratch scaler, you likely want one"); + console.warn("HIDController.jog_wheel - Non jog_scratch scaler, you likely want one"); } engine.scratchTick(deck, value); } else { @@ -1875,7 +1878,7 @@ class HIDController { if (scaler !== undefined) { value = scaler(active_group, "jog", value); } else { - HIDDebug("WARNING non jog scaler, you likely want one"); + console.warn("HIDController.jog_wheel - Non jog scaler, you likely want one"); } engine.setValue(active_group, "jog", value); } @@ -1890,7 +1893,7 @@ class HIDController { engine.stopTimer(this.timers[timer_id]); delete this.timers[timer_id]; } else { - // HIDDebug("No such autorepeat timer: " + timer_id); + // console.warn("HIDController.stopAutoRepeatTimer - No such autorepeat timer: " + timer_id); } } /** @@ -1905,7 +1908,7 @@ class HIDController { const packet = this.getInputPacket(this.defaultPacket); const field = packet.getField(group, name); if (field === undefined) { - HIDDebug("setAutoRepeat: field not found " + group + "." + name); + console.error("HIDController.setAutoRepeat - Field not found " + group + "." + name); return; } field.auto_repeat = callback; @@ -1974,7 +1977,7 @@ class HIDController { } } const new_group = this.resolveDeckGroup(deck); - HIDDebug("Switching to deck " + deck + " group " + new_group); + console.log("Switching to deck " + deck + " group " + new_group); if (this.disconnectDeck !== undefined) { this.disconnectDeck(); } @@ -1995,7 +1998,7 @@ class HIDController { controlgroup, bit.mapped_name, bit.mapped_callback, true); engine.connectControl(new_group, bit.mapped_name, bit.mapped_callback); var value = engine.getValue(new_group, bit.mapped_name); - HIDDebug("BIT " + bit.group + "." + bit.name + " value " + value); + console.log("Bit " + bit.group + "." + bit.name + " value " + value); if (value) { this.setOutput( bit.group, bit.name, @@ -2055,11 +2058,11 @@ class HIDController { let controlgroup; const field = this.getOutputField(group, name); if (field === undefined) { - HIDDebug("Linked output not found: " + group + "." + name); + console.error("HIDController.linkOutput - Linked output not found: " + group + "." + name); return; } if (field.mapped_group !== undefined) { - HIDDebug("Output already linked: " + field.mapped_group); + console.warn("HIDController.linkOutput - Output already linked: " + field.mapped_group); return; } controlgroup = this.resolveGroup(m_group); @@ -2089,11 +2092,11 @@ class HIDController { const field = this.getOutputField(group, name); let controlgroup; if (field === undefined) { - HIDDebug("Unlinked output not found: " + group + "." + name); + console.warn("HIDController.unlinkOutput - Output to be unlinked not found: " + group + "." + name); return; } if (field.mapped_group === undefined || field.mapped_name === undefined) { - HIDDebug("Unlinked output not mapped: " + group + "." + name); + console.warn("HIDController.unlinkOutput - Output to be unlinked not mapped: " + group + "." + name); return; } controlgroup = this.resolveGroup(field.mapped_group); @@ -2119,7 +2122,7 @@ class HIDController { setOutput(group, name, value, send_packet) { const field = this.getOutputField(group, name); if (field === undefined) { - HIDDebug("setOutput: unknown field: " + group + "." + name); + console.error("HIDController.setOutput - Unknown field: " + group + "." + name); return; } field.value = value << field.bit_offset; @@ -2143,7 +2146,7 @@ class HIDController { setOutputToggle(group, name, toggle_value) { const field = this.getOutputField(group, name); if (field === undefined) { - HIDDebug("setOutputToggle: unknown field " + group + "." + name); + console.error("HIDController.setOutputToggle - Unknown field " + group + "." + name); return; } field.value = toggle_value << field.bit_offset; From 43f55b696fcb88159649d99546fe6411b03f5933 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 23 Jul 2022 15:14:09 +0200 Subject: [PATCH 19/60] Made HIDDebug a function of the global-this object to prevent eslint error --- res/controllers/common-hid-packet-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 5d8facdbcf1..44e00f6949d 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -6,7 +6,7 @@ * @deprecated Use console.log instead * @param {any} message Message to be printed on controller debug console output */ -var HIDDebug = function(message) { +this.HIDDebug = function(message) { console.log("HID " + message); }; From 6bafa6d771a67aa7513507b9717dea25bcc6125a Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 23 Jul 2022 16:07:23 +0200 Subject: [PATCH 20/60] Use JSDoc @todo where applicable --- res/controllers/common-hid-packet-parser.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 44e00f6949d..0d18be39eee 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -306,6 +306,7 @@ 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 {number[]} 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. @@ -318,7 +319,6 @@ class HIDPacket { const bytes = this.packSizes[field.pack]; const signed = this.signedPackFormats.includes(field.pack); if (field.type === "bitvector") { - // TODO - fix multi byte bit vector outputs if (bytes > 1) { console.error("HIDPacket.pack - Packing multibyte bit vectors not yet supported"); return; @@ -1206,7 +1206,7 @@ class HIDController { /** * Find Output control matching give group and name * - * @todo The current implementation of this often called function is very slow anddoes not + * @todo The current implementation of this often called function is very slow and does not * scale, due to several nested loops. * @param {string} m_group Defines the group name for the field. The group can be any string, * but if it matches a valid Mixxx control group name, it is possible to map a field to a @@ -1365,8 +1365,7 @@ class HIDController { this.modifiers.set(modifier); } /** - * TODO - implement unlinking of modifiers - * + * @todo Implement unlinking of modifiers * @param {string} _group Unused * @param {string} _name Unused * @param _modifier Unused @@ -1414,8 +1413,7 @@ class HIDController { } } /** - * TODO - implement unlinking of controls - * + * @todo Implement unlinking of controls * @param {string} _group Defines the group name for the field. The group can be any string, but * if it matches a valid Mixxx control group name, it is possible to map a field to a * control or output without any additional code. From fefd3b3ffb8e1b5f3ddea0c4cdb946c970cbf555 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 23 Jul 2022 18:27:12 +0200 Subject: [PATCH 21/60] Fixed several errors --- res/controllers/common-hid-packet-parser.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 0d18be39eee..730127c3406 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -96,6 +96,7 @@ this.HIDDebug = function(message) { * - 'button' * - 'output' * @property {boolean} value + * @property {number} toggle */ /** @@ -106,7 +107,7 @@ this.HIDDebug = function(message) { * created manually. * * @property {number} size - * @property {bitObject{}} bits + * @property {bitObject[]} bits */ class HIDBitVector { constructor() { @@ -330,7 +331,7 @@ class HIDPacket { return; } - const value = (field.value !== undefined) ? field.value : 0; + const value = Number((field.value !== undefined) ? field.value : 0); if (value < field.min || value > field.max) { console.error("HIDPacket.pack - " + field.id + " packed value out of range: " + value); @@ -714,7 +715,6 @@ class HIDPacket { const control_group = this.getGroup(group, true); /** @type {packetField} */ let field; - let bitvector = undefined; const field_id = group + "." + name; if (control_group === undefined) { @@ -736,8 +736,8 @@ class HIDPacket { console.error("HIDPacket.addOutput - Overwrite non-bitmask control " + group + "." + name); return; } - bitvector = field.value; - bitvector.addOutputMask(group, name, bitmask); + let bitvector = field.value; + bitvector.addOutputMask(group, name, bitmask); return; } @@ -775,7 +775,7 @@ class HIDPacket { field.type = "bitvector"; field.id = group + "." + field_name; field.name = field_name; - bitvector = new HIDBitVector(); + let bitvector = new HIDBitVector(); bitvector.size = field.max; bitvector.addOutputMask(group, name, bitmask); field.value = bitvector; @@ -880,7 +880,7 @@ class HIDPacket { * @returns {HIDBitVector} List of modified bits (delta) */ parseBitVector(field, value) { - const bits = {}; + const bits = new HIDBitVector; let bit; let new_value; for (const bit_id in field.value.bits) { @@ -1156,7 +1156,7 @@ class HIDController { return undefined; } const str = group.replace(/\[Channel/, ""); - return str.substring(0, str.length - 1); + return Number(str.substring(0, str.length - 1)); } /** * Return the group name from given deck number. @@ -1913,7 +1913,7 @@ class HIDController { if (interval) { field.auto_repeat_interval = interval; } else { - field.auto_repeat_interval = controller.auto_repeat_interval; + field.auto_repeat_interval = this.auto_repeat_interval; } if (callback) { callback(field); From 52aaa47b60f994f47022660bba2765cb6c0c5328 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 23 Jul 2022 18:35:27 +0200 Subject: [PATCH 22/60] eslint: Make class instances const --- res/controllers/common-hid-packet-parser.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 730127c3406..841c38aff22 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -736,8 +736,8 @@ class HIDPacket { console.error("HIDPacket.addOutput - Overwrite non-bitmask control " + group + "." + name); return; } - let bitvector = field.value; - bitvector.addOutputMask(group, name, bitmask); + const bitvector = field.value; + bitvector.addOutputMask(group, name, bitmask); return; } @@ -775,7 +775,7 @@ class HIDPacket { field.type = "bitvector"; field.id = group + "." + field_name; field.name = field_name; - let bitvector = new HIDBitVector(); + const bitvector = new HIDBitVector(); bitvector.size = field.max; bitvector.addOutputMask(group, name, bitmask); field.value = bitvector; From ec75622787e9889501a6f258ff4590a582a324ce Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 23 Jul 2022 18:57:59 +0200 Subject: [PATCH 23/60] Fixed bug of duplicated variables for scratch ramp --- res/controllers/common-hid-packet-parser.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 841c38aff22..d7e482fa2d8 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1034,10 +1034,8 @@ this.HIDPacket = HIDPacket; * @property {number} scratchRPM RPM value for scratch_enable * @property {number} scratchAlpha Alpha value for scratch_enable * @property {number} scratchBeta Beta value for scratch_enable - * @property {boolean} scratchRampOnEnable UNUSED If 'ramp' is used when enabling scratch - * @property {boolean} scratchRampOnDisable UNUSED If 'ramp' is used when disabling scratch - * @property {boolean} rampedScratchEnable - * @property {boolean} rampedScratchDisable + * @property {boolean} scratchRampOnEnable Set true to ramp the deck speed down. Set false to stop instantly [default = false] + * @property {boolean} scratchRampOnDisable Set true to ramp the deck speed up. Set false to jump to normal play speed instantly [default = false] * @property {any} enableScratchCallback * @property {number} auto_repeat_interval Auto repeat interval default for fields, where not * specified individual @@ -1814,14 +1812,14 @@ class HIDController { this.scratchRPM, this.scratchAlpha, this.scratchBeta, - this.rampedScratchEnable + this.scratchRampOnEnable ); if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(true); } } else { this.isScratchEnabled = false; - engine.scratchDisable(deck, this.rampedScratchDisable); + engine.scratchDisable(deck, this.scratchRampOnDisable); if (this.enableScratchCallback !== undefined) { this.enableScratchCallback(false); } From b6595b53a5cb90414f8be0e835f5426214a27698 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 23 Jul 2022 19:17:29 +0200 Subject: [PATCH 24/60] Defined enableScratchCallback --- res/controllers/common-hid-packet-parser.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index d7e482fa2d8..509a1a51ca8 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -45,6 +45,13 @@ this.HIDDebug = function(message) { * @param {number} value Value to be scaled * @returns {number} Scaled value */ +/** + * 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 + */ /** * @typedef packetField * @type {object} @@ -1036,7 +1043,7 @@ this.HIDPacket = HIDPacket; * @property {number} scratchBeta Beta value for scratch_enable * @property {boolean} scratchRampOnEnable Set true to ramp the deck speed down. Set false to stop instantly [default = false] * @property {boolean} scratchRampOnDisable Set true to ramp the deck speed up. Set false to jump to normal play speed instantly [default = false] - * @property {any} enableScratchCallback + * @property {scratchingCallback} enableScratchCallback Callback function to call when, jog wheel scratching got enabled or disabled * @property {number} auto_repeat_interval Auto repeat interval default for fields, where not * specified individual */ @@ -1065,6 +1072,8 @@ class HIDController { this.scratchRampOnEnable = false; this.scratchRampOnDisable = false; + this.enableScratchCallback = undefined; + // Button states available this.buttonStates = {released: 0, pressed: 1}; // Output color values to send From 3ba41fe99dfbd868831ecc7222982ab832a69cb1 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 23 Jul 2022 19:55:12 +0200 Subject: [PATCH 25/60] Removed argument, which is not existing in constructor --- res/controllers/common-hid-packet-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 509a1a51ca8..b0f486d92ec 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -669,7 +669,7 @@ class HIDPacket { field.type = "bitvector"; field.name = field_name; field.id = group + "." + field_name; - bitvector = new HIDBitVector(field.max); + bitvector = new HIDBitVector(); bitvector.size = field.max; bitvector.addBitMask(group, name, bitmask); field.value = bitvector; From 8cb4082aaecfb259446f4436261d30bec04caaf4 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sat, 23 Jul 2022 20:29:29 +0200 Subject: [PATCH 26/60] Added @ts-ignore, to inhibit warnings for duplicate identifier --- res/controllers/common-hid-packet-parser.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index b0f486d92ec..cc3db9689ac 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -116,6 +116,7 @@ this.HIDDebug = function(message) { * @property {number} size * @property {bitObject[]} bits */ +// @ts-ignore Same identifier for class and instance needed for backward compatibility class HIDBitVector { constructor() { this.size = 0; @@ -203,6 +204,7 @@ class HIDBitVector { } } // Add class HIDBitVector to the Global JavaScript object +// @ts-ignore Same identifier for class and instance needed for backward compatibility this.HIDBitVector = HIDBitVector; @@ -212,6 +214,7 @@ this.HIDBitVector = HIDBitVector; * Wraps all defined modifiers to one object with uniform API. * Don't call directly, this is available as HIDController.modifiers */ +// @ts-ignore Same identifier for class and instance needed for backward compatibility class HIDModifierList { constructor() { this.modifiers = Object(); @@ -274,6 +277,7 @@ class HIDModifierList { } } // Add class HIDModifierList to the Global JavaScript object +// @ts-ignore Same identifier for class and instance needed for backward compatibility this.HIDModifierList = HIDModifierList; @@ -297,6 +301,7 @@ this.HIDModifierList = HIDModifierList; * of packet. Do NOT put the report ID in this; use * the reportId parameter instead. */ +// @ts-ignore Same identifier for class and instance needed for backward compatibility class HIDPacket { constructor(name, reportId = 0, callback, header) { this.name = name; @@ -1009,6 +1014,7 @@ class HIDPacket { } } // Add class HIDPacket to the Global JavaScript object +// @ts-ignore Same identifier for class and instance needed for backward compatibility this.HIDPacket = HIDPacket; @@ -1047,6 +1053,7 @@ this.HIDPacket = HIDPacket; * @property {number} auto_repeat_interval Auto repeat interval default for fields, where not * specified individual */ +// @ts-ignore Same identifier for class and instance needed for backward compatibility class HIDController { constructor() { this.initialized = false; @@ -2160,4 +2167,5 @@ class HIDController { } } // Add class HIDController to the Global JavaScript object +// @ts-ignore Same identifier for class and instance needed for backward compatibility this.HIDController = HIDController; From 1a3d81f361ac5cdd0ab8d319b0225f26c3937f2a Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 08:55:55 +0200 Subject: [PATCH 27/60] Corrected type that parseBitVector returns --- res/controllers/common-hid-packet-parser.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index cc3db9689ac..6300788c7a6 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -889,10 +889,11 @@ class HIDPacket { * @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. - * @returns {HIDBitVector} List of modified bits (delta) + * @returns {bitObject[]} List of modified bits (delta) */ parseBitVector(field, value) { - const bits = new HIDBitVector; + /** @type bitObject[]*/ + let bits; let bit; let new_value; for (const bit_id in field.value.bits) { From 06bae213ffaffc021f7665b1cb5556caeda9bd07 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 09:47:09 +0200 Subject: [PATCH 28/60] Replaced wrong eslintrc keyword "readable" by "readonly". https://eslint.org/docs/latest/user-guide/configuring/language-options#using-configuration-files-1 --- .eslintrc.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index f70c00d7935..ee727c2b306 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -62,7 +62,7 @@ "yoda": "warn" }, "globals": { - "console": "readable" + "console": "readonly" }, "overrides": [ { @@ -74,7 +74,7 @@ { "files": ["res/controllers/common-hid-packet-parser.js"], "globals": { - "console": "readable", + "console": "readonly", "print": "writable" } }, @@ -82,7 +82,7 @@ "files": ["res/controllers/*.js"], "excludedFiles": "res/controllers/common-hid-packet-parser.js", "globals": { - "console": "readable", + "console": "readonly", "svg": "writable", "HIDController": "writable", "HIDDebug": "writable", From 801abf549d4586b7b0886a06a8397a1180c47c8b Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 10:17:06 +0200 Subject: [PATCH 29/60] Adjusted eslint rules for common-hid-packet-parser.js: -External function print is no longer used -Disabled camelcase warning, because we can't change the keywords due to backward compatibility --- .eslintrc.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index ee727c2b306..f264b0ea417 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -74,8 +74,10 @@ { "files": ["res/controllers/common-hid-packet-parser.js"], "globals": { - "console": "readonly", - "print": "writable" + "console": "readonly" + }, + "rules": { + "camelcase": "off" } }, { From 796e8e709eddf7091778a80baf3c25f5f2c0c746 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 10:27:57 +0200 Subject: [PATCH 30/60] Corrected boolean <-> number conversion --- res/controllers/common-hid-packet-parser.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 6300788c7a6..86de64d891d 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1780,7 +1780,7 @@ class HIDController { if (value === this.buttonStates.released) { return; } - const status = engine.getValue(group, control) !== true; + const status = Boolean(engine.getValue(group, control)) !== true; engine.setValue(group, control, status); } /** @@ -2084,9 +2084,9 @@ class HIDController { field.mapped_callback = callback; engine.connectControl(controlgroup, m_name, callback); if (engine.getValue(controlgroup, m_name)) { - this.setOutput(m_group, m_name, "on"); + this.setOutput(m_group, m_name, true); } else { - this.setOutput(m_group, m_name, "off"); + this.setOutput(m_group, m_name, false); } } /** @@ -2128,7 +2128,7 @@ class HIDController { * but if it matches a valid Mixxx control name in the group defined for field, the system * attempts to attach it directly to the correct field. Together group and name form the ID * of the field (group.name) - * @param {number} value Value to set as new output state of the control + * @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 * immediately */ @@ -2138,8 +2138,8 @@ class HIDController { console.error("HIDController.setOutput - Unknown field: " + group + "." + name); return; } - field.value = value << field.bit_offset; - field.toggle = value << field.bit_offset; + field.value = Number(value) << field.bit_offset; + field.toggle = Number(value) << field.bit_offset; if (send_packet) { field.packet.send(); } From 3c2ce07fd55b1f8334d623779474f8c9255a3b59 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 11:52:08 +0200 Subject: [PATCH 31/60] Added guards for safe handling field.value as HIDBitVector --- res/controllers/common-hid-packet-parser.js | 63 ++++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 86de64d891d..34dea7acdc5 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -332,12 +332,13 @@ class HIDPacket { const bytes = this.packSizes[field.pack]; const signed = this.signedPackFormats.includes(field.pack); if (field.type === "bitvector") { + const bitVector = /** @type {HIDBitVector} */ (field.value); if (bytes > 1) { console.error("HIDPacket.pack - Packing multibyte bit vectors not yet supported"); return; } - for (const bit_id in field.value.bits) { - const bit = field.value.bits[bit_id]; + for (const bit_id in bitVector.bits) { + const bit = bitVector.bits[bit_id]; data[field.offset] = data[field.offset] | bit.value; } return; @@ -538,9 +539,14 @@ class HIDPacket { console.error("HIDPacket.lookupBit - Bitvector match not found: " + group + "." + name); return undefined; } + if (field.type !== "bitvector") { + console.error("HIDPacket.lookupBit - Control doesn't refer a field of type bitvector: " + group + "." + name); + return undefined; + } + const bitVector = /** @type {HIDBitVector} */ (field.value); const bit_id = group + "." + name; - for (const bit_name in field.value.bits) { - const bit = field.value.bits[bit_name]; + for (const bit_name in bitVector.bits) { + const bit = bitVector.bits[bit_name]; if (bit.id === bit_id) { return bit; } @@ -610,18 +616,23 @@ class HIDPacket { console.error("HIDPacket.addControl - Trying to overwrite non-bitmask control " + group + " " + name); return; } - bitvector = field.value; - bitvector.addBitMask(group, name, bitmask); - if (callback !== undefined) { - if (typeof callback !== "function") { - console.error( - "HIDPacket.addControl - Callback provided for " + group + "." + name + - " is not a function."); - return; + if (field.type !== "bitvector") { + console.error("HIDPacket.addControl - Field is not of type bitvector: " + group + "." + name); + return undefined; + } else { + const bitVector = /** @type {HIDBitVector} */ (field.value); + bitVector.addBitMask(group, name, bitmask); + if (callback !== undefined) { + if (typeof callback !== "function") { + console.error( + "HIDPacket.addControl - Callback provided for " + group + "." + name + + " is not a function."); + return; + } + this.setCallback(group, name, callback); } - this.setCallback(group, name, callback); + return; } - return; } field = {}; @@ -748,8 +759,12 @@ class HIDPacket { console.error("HIDPacket.addOutput - Overwrite non-bitmask control " + group + "." + name); return; } - const bitvector = field.value; - bitvector.addOutputMask(group, name, bitmask); + if (field.type !== "bitvector") { + console.error("HIDPacket.addOutput - Field is not of type bitvector: " + group + "." + name); + return; + } + const bitVector = /** @type {HIDBitVector} */ (field.value); + bitVector.addOutputMask(group, name, bitmask); return; } @@ -823,8 +838,9 @@ class HIDPacket { return; } if (field.type === "bitvector") { - for (const bit_id in field.value.bits) { - const bit = field.value.bits[bit_id]; + const bitVector = /** @type {HIDBitVector} */ (field.value); + for (const bit_id in bitVector.bits) { + const bit = bitVector.bits[bit_id]; if (bit_id !== field_id) { continue; } @@ -893,11 +909,16 @@ class HIDPacket { */ parseBitVector(field, value) { /** @type bitObject[]*/ - let bits; + const bits = new Array(); let bit; let new_value; - for (const bit_id in field.value.bits) { - bit = field.value.bits[bit_id]; + if (field.type !== "bitvector") { + console.error("HIDPacket.parseBitVector - Field isn't of type bitvector"); + return undefined; + } + const bitVector = /** @type {HIDBitVector} */ (field.value); + for (const bit_id in bitVector.bits) { + bit = bitVector.bits[bit_id]; new_value = (bit.bitmask & value) >> bit.bit_offset; if (bit.value !== undefined && bit.value !== new_value) { bits[bit_id] = bit; From 6d0c7eaf0be34bba81c0f126d9251e2ef887d402 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 12:23:20 +0200 Subject: [PATCH 32/60] Made variables const --- res/controllers/common-hid-packet-parser.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 34dea7acdc5..4d95a442551 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -2031,7 +2031,7 @@ class HIDController { engine.connectControl( controlgroup, bit.mapped_name, bit.mapped_callback, true); engine.connectControl(new_group, bit.mapped_name, bit.mapped_callback); - var value = engine.getValue(new_group, bit.mapped_name); + const value = engine.getValue(new_group, bit.mapped_name); console.log("Bit " + bit.group + "." + bit.name + " value " + value); if (value) { this.setOutput( @@ -2054,7 +2054,7 @@ class HIDController { engine.connectControl( controlgroup, field.mapped_name, field.mapped_callback, true); engine.connectControl(new_group, field.mapped_name, field.mapped_callback); - value = engine.getValue(new_group, field.mapped_name); + const value = engine.getValue(new_group, field.mapped_name); if (value) { this.setOutput( field.group, field.name, @@ -2089,7 +2089,6 @@ class HIDController { * @param {controlCallback} callback Callback function for the control */ linkOutput(group, name, m_group, m_name, callback) { - let controlgroup; const field = this.getOutputField(group, name); if (field === undefined) { console.error("HIDController.linkOutput - Linked output not found: " + group + "." + name); @@ -2099,7 +2098,7 @@ class HIDController { console.warn("HIDController.linkOutput - Output already linked: " + field.mapped_group); return; } - controlgroup = this.resolveGroup(m_group); + const controlgroup = this.resolveGroup(m_group); field.mapped_group = m_group; field.mapped_name = m_name; field.mapped_callback = callback; @@ -2124,7 +2123,6 @@ class HIDController { */ unlinkOutput(group, name, callback) { const field = this.getOutputField(group, name); - let controlgroup; if (field === undefined) { console.warn("HIDController.unlinkOutput - Output to be unlinked not found: " + group + "." + name); return; @@ -2133,7 +2131,7 @@ class HIDController { console.warn("HIDController.unlinkOutput - Output to be unlinked not mapped: " + group + "." + name); return; } - controlgroup = this.resolveGroup(field.mapped_group); + const controlgroup = this.resolveGroup(field.mapped_group); engine.connectControl(controlgroup, field.mapped_name, callback, true); field.mapped_group = undefined; field.mapped_name = undefined; From 49faef947cdfdc1b0a75ac1a4e2465663f505468 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 12:38:16 +0200 Subject: [PATCH 33/60] Reduced scope of change variable and initialize it (in one branch it remained undefined, but was used for comparison later) --- res/controllers/common-hid-packet-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 4d95a442551..70a3daec4b4 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -971,7 +971,7 @@ class HIDPacket { field.value = value; continue; } - var change; + let change = 0; if (field.isEncoder) { if (field.value === field.max && value === field.min) { change = 1; From e1b71ce5bc25d57796d1422baafb4db3a2ec5e6f Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 12:45:35 +0200 Subject: [PATCH 34/60] addControl has no return type -> Do not return undefined --- res/controllers/common-hid-packet-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 70a3daec4b4..09a707c80d8 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -618,7 +618,7 @@ class HIDPacket { } if (field.type !== "bitvector") { console.error("HIDPacket.addControl - Field is not of type bitvector: " + group + "." + name); - return undefined; + return; } else { const bitVector = /** @type {HIDBitVector} */ (field.value); bitVector.addBitMask(group, name, bitmask); From 2a63ac5ded8ba8385a7251937c9a001b0441bf17 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 12:56:05 +0200 Subject: [PATCH 35/60] Added missing type and docu for pack --- res/controllers/common-hid-packet-parser.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 09a707c80d8..4533c9d37fa 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -588,7 +588,13 @@ class HIDPacket { * position 0 * - For HID devices which use ReportIDs to enumerate the reports, the * data bytes starts at position 1 - * @param pack control packing format for unpack(), one of b/B, h/H, i/I + * @param {object} pack Is one of the field packing types: + * - b signed byte + * - B unsigned byte + * - h signed short + * - H unsigned short + * - i signed integer + * - I unsigned integer * @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 @@ -729,7 +735,13 @@ class HIDPacket { * position 0 * - For HID devices which use ReportIDs to enumerate the reports, the * data bytes starts at position 1 - * @param pack control packing format for pack(), one of b/B, h/H, i/I + * @param {object} pack Is one of the field packing types: + * - b signed byte + * - B unsigned byte + * - h signed short + * - H unsigned short + * - i signed integer + * - I unsigned integer * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are * considered. * @param {controlCallback} [callback=undefined] Callback function for the control From 11202e829971d084982e192b23ba374060a0c50d Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 13:27:56 +0200 Subject: [PATCH 36/60] Do not try to initialize field structure again, after usage --- res/controllers/common-hid-packet-parser.js | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 4533c9d37fa..149d9ffb867 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -615,18 +615,18 @@ class HIDPacket { return; } - let field = this.getFieldByOffset(offset, pack); - if (field !== undefined) { + const fieldByOffset = this.getFieldByOffset(offset, pack); + if (fieldByOffset !== undefined) { if (bitmask === undefined) { console.error("HIDPacket.addControl - Registering offset " + offset + " pack " + pack); console.error("HIDPacket.addControl - Trying to overwrite non-bitmask control " + group + " " + name); return; } - if (field.type !== "bitvector") { + if (fieldByOffset.type !== "bitvector") { console.error("HIDPacket.addControl - Field is not of type bitvector: " + group + "." + name); return; } else { - const bitVector = /** @type {HIDBitVector} */ (field.value); + const bitVector = /** @type {HIDBitVector} */ (fieldByOffset.value); bitVector.addBitMask(group, name, bitmask); if (callback !== undefined) { if (typeof callback !== "function") { @@ -641,7 +641,8 @@ class HIDPacket { } } - field = {}; + /** @type {packetField} */ + const field = {}; field.packet = undefined; field.id = group + "." + name; field.group = group; @@ -748,8 +749,6 @@ class HIDPacket { */ addOutput(group, name, offset, pack, bitmask, callback) { const control_group = this.getGroup(group, true); - /** @type {packetField} */ - let field; const field_id = group + "." + name; if (control_group === undefined) { @@ -765,22 +764,23 @@ class HIDPacket { offset -= 1; // Check if we are adding a Output bit to existing bitvector - field = this.getFieldByOffset(offset, pack); - if (field !== undefined) { + const fieldByOffset = this.getFieldByOffset(offset, pack); + if (fieldByOffset !== undefined) { if (bitmask === undefined) { console.error("HIDPacket.addOutput - Overwrite non-bitmask control " + group + "." + name); return; } - if (field.type !== "bitvector") { + if (fieldByOffset.type !== "bitvector") { console.error("HIDPacket.addOutput - Field is not of type bitvector: " + group + "." + name); return; } - const bitVector = /** @type {HIDBitVector} */ (field.value); + const bitVector = /** @type {HIDBitVector} */ (fieldByOffset.value); bitVector.addOutputMask(group, name, bitmask); return; } - field = {}; + /** @type {packetField} */ + const field = {}; field.id = field_id; field.group = group; field.name = name; From 02042528ae21f9e971eb7098ca42fc91df38ff89 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 13:42:41 +0200 Subject: [PATCH 37/60] Corrected wrong type syntax --- res/controllers/common-hid-packet-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 149d9ffb867..2d65602104a 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -920,7 +920,7 @@ class HIDPacket { * @returns {bitObject[]} List of modified bits (delta) */ parseBitVector(field, value) { - /** @type bitObject[]*/ + /** @type {bitObject[]}*/ const bits = new Array(); let bit; let new_value; From 109d2eafdb3b56ea79996df664f47c12f6036d1a Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 14:05:06 +0200 Subject: [PATCH 38/60] Specified missing type --- res/controllers/common-hid-packet-parser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 2d65602104a..5f2524dfde1 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -945,7 +945,7 @@ class HIDPacket { * BitVectors are returned as bits you can iterate separately. * * @param {number[]} data Data received as InputReport from the device - * @returns List of changed fields with new value. + * @returns {packetField|bitObject} List of changed fields with new value. */ parse(data) { const field_changes = {}; @@ -1614,7 +1614,7 @@ class HIDController { * fields in default mixxx groups. Not done if a callback was defined. * * @param packet Unused - * @param delta + * @param {packetField|bitObject} delta */ processIncomingPacket(packet, delta) { /** @type {packetField} */ From 31bc9ffd270b484cc6c98c219803c6a6fe83dfb2 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 15:06:54 +0200 Subject: [PATCH 39/60] Separated types bitObject / packetField --- res/controllers/common-hid-packet-parser.js | 48 +++++++++++---------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 5f2524dfde1..b9294560189 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -24,7 +24,7 @@ this.HIDDebug = function(message) { * Callback function to call when, data for specified filed in the packet is updated. * * @callback controlCallback - * @param {packetField} field Object that describes a field inside of a packet, which can often + * @param {packetField|bitObject} Object that describes a field/bit inside of a packet, which can often * mapped to a Mixxx control. */ /** @@ -74,9 +74,8 @@ this.HIDDebug = function(message) { * @property {number} auto_repeat_interval * @property {number} min * @property {number} max - * @property {('bitvector'|'button'|'control'|'output')} type Must be either: + * @property {('bitvector'|'control'|'output')} type Must be either: * - 'bitvector' If value is of type HIDBitVector - * - 'button' If value is a boolean * - 'control' If value is a number * - 'output' * @property {HIDBitVector|boolean|number} value @@ -945,10 +944,11 @@ class HIDPacket { * BitVectors are returned as bits you can iterate separately. * * @param {number[]} data Data received as InputReport from the device - * @returns {packetField|bitObject} List of changed fields with new value. + * @returns {packetField[]|bitObject[]} List of changed fields with new value. */ parse(data) { - const field_changes = {}; + /** @type {packetField[]|bitObject[]}*/ + const field_changes = new Array(); let group; let group_name; let field; @@ -1544,7 +1544,6 @@ class HIDController { parsePacket(data, length) { /** @type {HIDPacket} */ let packet; - let changed_data; if (this.InputPackets === undefined) { return; } @@ -1571,7 +1570,7 @@ class HIDController { continue; } } - changed_data = packet.parse(data); + const changed_data = packet.parse(data); if (packet.callback !== undefined) { packet.callback(packet, changed_data); return; @@ -1614,17 +1613,16 @@ class HIDController { * fields in default mixxx groups. Not done if a callback was defined. * * @param packet Unused - * @param {packetField|bitObject} delta + * @param {packetField[]|bitObject[]} delta */ processIncomingPacket(packet, delta) { /** @type {packetField} */ - let field; for (const name in delta) { if (this.ignoredControlChanges !== undefined && this.ignoredControlChanges.indexOf(name) !== -1) { continue; } - field = delta[name]; + const field = delta[name]; if (field.type === "button") { // Button/Boolean field this.processButton(field); @@ -1639,7 +1637,7 @@ class HIDController { /** * Get active group for this field * - * @param {packetField} field Object that describes a field inside of a packet, which can often + * @param {packetField|bitObject} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. * @returns {string} Group */ @@ -1662,7 +1660,7 @@ class HIDController { /** * Get active control name from field * - * @param {packetField} field Object that describes a field inside of a packet, which can often + * @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 */ @@ -1676,7 +1674,7 @@ class HIDController { /** * Process given button field, triggering events * - * @param {packetField} field Object that describes a field inside of a packet, which can often + * @param {bitObject} field Object that describes a field inside of a packet, which can often * mapped to a Mixxx control. */ processButton(field) { @@ -1692,7 +1690,7 @@ class HIDController { } if (group === "modifiers") { - if (field.value !== 0) { + if (field.value !== false) { this.modifiers.set(control, true); } else { this.modifiers.set(control, false); @@ -1713,7 +1711,7 @@ class HIDController { } if (control === "jog_touch") { if (group !== undefined) { - if (field.value === this.buttonStates.pressed) { + if (field.value === Boolean(this.buttonStates.pressed)) { this.enableScratch(group, true); } else { this.enableScratch(group, false); @@ -1722,12 +1720,12 @@ class HIDController { return; } if (this.toggleButtons.indexOf(control) !== -1) { - if (field.value === this.buttonStates.released) { + if (field.value === Boolean(this.buttonStates.released)) { return; } if (engine.getValue(group, control)) { if (control === "play") { - engine.setValue(group, "stop", true); + engine.setValue(group, "stop", 1); } else { engine.setValue(group, control, false); } @@ -1736,10 +1734,10 @@ class HIDController { } return; } - if (field.auto_repeat && field.value === this.buttonStates.pressed) { + if (field.auto_repeat && field.value === Boolean(this.buttonStates.pressed)) { console.log("Callback for " + field.group); engine.setValue(group, control, field.auto_repeat(field)); - } else if (engine.getValue(group, control) === false) { + } else if (engine.getValue(group, control) === 0) { engine.setValue(group, control, true); } else { engine.setValue(group, control, false); @@ -1757,19 +1755,23 @@ class HIDController { const control = this.getActiveFieldControl(field); if (group === undefined) { - console.warn( + console.error( "HIDController.processControl - Could not resolve group from " + field.group + " " + field.mapped_group + " " + field.name + " " + field.mapped_name); return; } + if (field.type === "bitvector") { + console.error("HIDController.processControl - Control refers a field of type bitvector: " + group + "." + control); + return; + } if (field.callback !== undefined) { value = field.callback(field); return; } if (group === "modifiers") { - this.modifiers.set(control, field.value); + this.modifiers.set(control, Number(field.value)); return; } if (control === "jog_wheel") { @@ -1788,7 +1790,7 @@ class HIDController { engine.setValue(group, control, field_delta); } else { if (scaler !== undefined) { - value = scaler(group, control, value); + value = scaler(group, control, Number(value)); // See the Traktor S4 script for how to use this. If the scaler function has this // parameter set to true, we use the effects-engine setParameter call instead of // setValue. @@ -1797,7 +1799,7 @@ class HIDController { return; } } - engine.setValue(group, control, value); + engine.setValue(group, control, Number(value)); } } /** From 6b311185b13f580a32d5ffa05ca60cf45871a8a0 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 15:35:27 +0200 Subject: [PATCH 40/60] Use LED-colors as On / Off values in linkOutput --- res/controllers/common-hid-packet-parser.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index b9294560189..8eac5a85b00 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -2118,9 +2118,9 @@ class HIDController { field.mapped_callback = callback; engine.connectControl(controlgroup, m_name, callback); if (engine.getValue(controlgroup, m_name)) { - this.setOutput(m_group, m_name, true); + this.setOutput(m_group, m_name, this.LEDColors.on); } else { - this.setOutput(m_group, m_name, false); + this.setOutput(m_group, m_name, this.LEDColors.off); } } /** From 3c7c70a7aedd81431d213fa7c293256d54a113a2 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 17:49:57 +0200 Subject: [PATCH 41/60] Determine size of output array before initializing it, and initialize it with the final size Use Uint8Array as type for input and output reports Use sendOutputReport with Uint8Array buffer as argument --- res/controllers/common-hid-packet-parser.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 8eac5a85b00..4ded9a5c2ae 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -308,6 +308,7 @@ class HIDPacket { this.callback = callback; this.reportId = reportId; this.groups = {}; + this.length = 0; // Size of various 'pack' values in bytes this.packSizes = {b: 1, B: 1, h: 2, H: 2, i: 4, I: 4}; @@ -319,7 +320,7 @@ class HIDPacket { * Can only pack bits and byte values, patches welcome. * * @todo Implement multi byte bit vector outputs - * @param {number[]} data Data received as InputReport from the device + * @param {Uint8Array} 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. */ @@ -373,7 +374,7 @@ class HIDPacket { * - i signed integer * - I unsigned integer * - * @param {number[]} data Data received as InputReport from the device + * @param {Uint8Array} 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 @@ -775,6 +776,7 @@ class HIDPacket { } const bitVector = /** @type {HIDBitVector} */ (fieldByOffset.value); bitVector.addOutputMask(group, name, bitmask); + if (this.length < offset) { this.length = offset; } return; } @@ -822,6 +824,7 @@ class HIDPacket { } // Add Output to HID packet + if (this.length < field.end_offset) { this.length = field.end_offset; } control_group[field.id] = field; } /** @@ -943,7 +946,7 @@ class HIDPacket { * Data is expected to be a Packet() received from HID device. * BitVectors are returned as bits you can iterate separately. * - * @param {number[]} data Data received as InputReport from the device + * @param {Uint8Array} data Data received as InputReport from the device * @returns {packetField[]|bitObject[]} List of changed fields with new value. */ parse(data) { @@ -1018,7 +1021,7 @@ class HIDPacket { * @param {boolean} [debug=false] Enables debug output to console */ send(debug) { - const data = []; + const data = new Uint8Array(this.length); if (this.header !== undefined) { for (let header_byte = 0; header_byte < this.header.length; header_byte++) { @@ -1044,7 +1047,7 @@ class HIDPacket { } console.log("Sending packet with Report ID " + this.reportId + ": " + packet_string); } - controller.send(data, data.length, this.reportId); + controller.sendOutputReport(this.reportId, data.buffer); } } // Add class HIDPacket to the Global JavaScript object @@ -1536,7 +1539,7 @@ class HIDController { * - Calls processIncomingPacket and processes automated events there. * - If defined, calls processDelta for results after processing automated fields * - * @param {number[]} data The data received from an HID InputReport. + * @param {Uint8Array} 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 From aafed4f201f3437d3a3fe2d4680c2ced80ea457e Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 21:44:48 +0200 Subject: [PATCH 42/60] Reduced scope of variables and made them const where possible --- res/controllers/common-hid-packet-parser.js | 104 ++++++++------------ 1 file changed, 42 insertions(+), 62 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 4ded9a5c2ae..da48542f53a 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -380,15 +380,15 @@ class HIDPacket { * @returns {number} Value for the field in data, represented according the fields packing type */ unpack(data, field) { - let value = 0; if (!(field.pack in this.packSizes)) { console.error("HIDPacket.unpack - Parsing packed value: invalid pack format " + field.pack); - return; + return undefined; } const bytes = this.packSizes[field.pack]; const signed = this.signedPackFormats.includes(field.pack); + let value = 0; for (let field_byte = 0; field_byte < bytes; field_byte++) { if (data[field.offset + field_byte] === 255 && field_byte === 4) { value += 0; @@ -450,12 +450,11 @@ class HIDPacket { return undefined; } const end_offset = offset + this.packSizes[pack]; - let group; - let field; + for (const group_name in this.groups) { - group = this.groups[group_name]; + const group = this.groups[group_name]; for (const field_id in group) { - field = group[field_id]; + const field = group[field_id]; // Same field offset if (field.offset === offset) { return field; @@ -497,16 +496,16 @@ class HIDPacket { return undefined; } - let control_group = this.groups[group]; - if (field_id in control_group) { - return control_group[field_id]; + const control_group1 = this.groups[group]; + if (field_id in control_group1) { + return control_group1[field_id]; } // Lookup for bit fields in bitvector matching field name for (const group_name in this.groups) { - control_group = this.groups[group_name]; - for (const field_name in control_group) { - const field = control_group[field_name]; + const control_group2 = this.groups[group_name]; + for (const field_name in control_group2) { + const field = control_group2[field_name]; if (field === undefined || field.type !== "bitvector") { continue; } @@ -605,7 +604,6 @@ class HIDPacket { */ addControl(group, name, offset, pack, bitmask, isEncoder, callback) { const control_group = this.getGroup(group, true); - let bitvector = undefined; if (control_group === undefined) { console.error("HIDPacket.addControl - Creating HID packet group " + group); return; @@ -692,7 +690,7 @@ class HIDPacket { field.type = "bitvector"; field.name = field_name; field.id = group + "." + field_name; - bitvector = new HIDBitVector(); + const bitvector = new HIDBitVector(); bitvector.size = field.max; bitvector.addBitMask(group, name, bitmask); field.value = bitvector; @@ -924,16 +922,15 @@ class HIDPacket { parseBitVector(field, value) { /** @type {bitObject[]}*/ const bits = new Array(); - let bit; - let new_value; + if (field.type !== "bitvector") { console.error("HIDPacket.parseBitVector - Field isn't of type bitvector"); return undefined; } const bitVector = /** @type {HIDBitVector} */ (field.value); for (const bit_id in bitVector.bits) { - bit = bitVector.bits[bit_id]; - new_value = (bit.bitmask & value) >> bit.bit_offset; + const bit = bitVector.bits[bit_id]; + const new_value = (bit.bitmask & value) >> bit.bit_offset; if (bit.value !== undefined && bit.value !== new_value) { bits[bit_id] = bit; } @@ -952,15 +949,11 @@ class HIDPacket { parse(data) { /** @type {packetField[]|bitObject[]}*/ const field_changes = new Array(); - let group; - let group_name; - let field; - let field_id; - for (group_name in this.groups) { - group = this.groups[group_name]; - for (field_id in group) { - field = group[field_id]; + for (const group_name in this.groups) { + const group = this.groups[group_name]; + for (const field_id in group) { + const field = group[field_id]; if (field === undefined) { continue; } @@ -1439,13 +1432,12 @@ class HIDController { * @param {controlCallback} callback Callback function for the control */ linkControl(group, name, m_group, m_name, callback) { - let field; const packet = this.getInputPacket(this.defaultPacket); if (packet === undefined) { console.error("HIDController.linkControl - Creating modifier: input packet " + this.defaultPacket + " not found"); return; } - field = packet.getField(group, name); + let field = packet.getField(group, name); if (field === undefined) { console.error("HIDController.linkControl - Field not found: " + group + "." + name); return; @@ -1545,13 +1537,12 @@ class HIDController { * @param {number} length Length of the data array in bytes */ parsePacket(data, length) { - /** @type {HIDPacket} */ - let packet; if (this.InputPackets === undefined) { return; } for (const name in this.InputPackets) { - packet = this.InputPackets[name]; + /** @type {HIDPacket} */ + let packet = this.InputPackets[name]; // When the device uses ReportIDs to enumerate the reports, hidapi // prepends the report ID to the data sent to Mixxx. If the device @@ -1753,7 +1744,6 @@ class HIDController { * mapped to a Mixxx control. */ processControl(field) { - let value; const group = this.getActiveFieldGroup(field); const control = this.getActiveFieldControl(field); @@ -1770,7 +1760,7 @@ class HIDController { } if (field.callback !== undefined) { - value = field.callback(field); + /*value =*/field.callback(field); return; } if (group === "modifiers") { @@ -1783,7 +1773,7 @@ class HIDController { return; } // Call value scaler if defined and send mixxx signal - value = field.value; + let value = field.value; const scaler = this.getScaler(control); if (field.isEncoder) { let field_delta = field.delta; @@ -1901,7 +1891,6 @@ class HIDController { * mapped to a Mixxx control. */ jog_wheel(field) { - let scaler = undefined; const active_group = this.getActiveFieldGroup(field); let value = undefined; if (field.isEncoder) { @@ -1914,9 +1903,9 @@ class HIDController { if (deck === undefined) { return; } - scaler = this.getScaler("jog_scratch"); - if (scaler !== undefined) { - value = scaler(active_group, "jog_scratch", value); + const jogScratchScaler = this.getScaler("jog_scratch"); + if (jogScratchScaler !== undefined) { + value = jogScratchScaler(active_group, "jog_scratch", value); } else { console.warn("HIDController.jog_wheel - Non jog_scratch scaler, you likely want one"); } @@ -1925,9 +1914,9 @@ class HIDController { if (active_group === undefined) { return; } - scaler = this.getScaler("jog"); - if (scaler !== undefined) { - value = scaler(active_group, "jog", value); + const jogScaler = this.getScaler("jog"); + if (jogScaler !== undefined) { + value = jogScaler(active_group, "jog", value); } else { console.warn("HIDController.jog_wheel - Non jog scaler, you likely want one"); } @@ -1979,25 +1968,19 @@ class HIDController { * callback call namespaces and 'this' reference */ autorepeatTimer() { - let group_name; - let group; - let field; - let field_name; - let bit_name; - let bit; const packet = this.InputPackets[this.defaultPacket]; - for (group_name in packet.groups) { - group = packet.groups[group_name]; - for (field_name in group) { - field = group[field_name]; + for (const group_name in packet.groups) { + const group = packet.groups[group_name]; + for (const field_name in group) { + const field = group[field_name]; if (field.type !== "bitvector") { if (field.auto_repeat) { this.processControl(field); } continue; } - for (bit_name in field.value.bits) { - bit = field.value.bits[bit_name]; + for (const bit_name in field.value.bits) { + const bit = field.value.bits[bit_name]; if (bit.auto_repeat) { this.processButton(bit); } @@ -2011,9 +1994,6 @@ class HIDController { * @param {number} deck Number of deck */ switchDeck(deck) { - let packet; - let field; - let controlgroup; if (deck === undefined) { if (this.activeDeck === undefined) { deck = 1; @@ -2033,20 +2013,20 @@ class HIDController { this.disconnectDeck(); } for (const packet_name in this.OutputPackets) { - packet = this.OutputPackets[packet_name]; + const packet = this.OutputPackets[packet_name]; for (const group_name in packet.groups) { const group = packet.groups[group_name]; for (const field_name in group) { - field = group[field_name]; + const field = group[field_name]; if (field.type === "bitvector") { for (const bit_id in field.value.bits) { const bit = field.value.bits[bit_id]; if (this.virtualDecks.indexOf(bit.mapped_group) === -1) { continue; } - controlgroup = this.resolveGroup(bit.mapped_group); + const bitControlGroup = this.resolveGroup(bit.mapped_group); engine.connectControl( - controlgroup, bit.mapped_name, bit.mapped_callback, true); + bitControlGroup, bit.mapped_name, bit.mapped_callback, true); engine.connectControl(new_group, bit.mapped_name, bit.mapped_callback); const value = engine.getValue(new_group, bit.mapped_name); console.log("Bit " + bit.group + "." + bit.name + " value " + value); @@ -2067,9 +2047,9 @@ class HIDController { if (this.virtualDecks.indexOf(field.mapped_group) === -1) { continue; } - controlgroup = this.resolveGroup(field.mapped_group); + const fieldControlGroup = this.resolveGroup(field.mapped_group); engine.connectControl( - controlgroup, field.mapped_name, field.mapped_callback, true); + fieldControlGroup, field.mapped_name, field.mapped_callback, true); engine.connectControl(new_group, field.mapped_name, field.mapped_callback); const value = engine.getValue(new_group, field.mapped_name); if (value) { From 2ef4006c7ab75a823379480b0e7e26d42c9281b9 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Sun, 24 Jul 2022 21:56:18 +0200 Subject: [PATCH 43/60] Corrected return type of lookupBit --- res/controllers/common-hid-packet-parser.js | 32 ++++++++++++--------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index da48542f53a..f33bc20a807 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -530,7 +530,7 @@ class HIDPacket { * but if it matches a valid Mixxx control name in the group defined for field, the system * attempts to attach it directly to the correct field. Together group and name form the ID * of the field (group.name) - * @returns {packetField} Reference to a bit in a bitvector field + * @returns {bitObject} Reference to a bit in a bitvector field */ lookupBit(group, name) { const field = this.getField(group, name); @@ -1399,13 +1399,13 @@ class HIDController { return; } const bit_id = group + "." + name; - const field = packet.lookupBit(group, name); - if (field === undefined) { + const bitField = packet.lookupBit(group, name); + if (bitField === undefined) { console.error("HIDController.linkModifier - Bit field not found: " + bit_id); return; } - field.group = "modifiers"; - field.name = modifier; + bitField.group = "modifiers"; + bitField.name = modifier; this.modifiers.set(modifier); } /** @@ -1437,22 +1437,28 @@ class HIDController { console.error("HIDController.linkControl - Creating modifier: input packet " + this.defaultPacket + " not found"); return; } - let field = packet.getField(group, name); + const field = packet.getField(group, name); if (field === undefined) { console.error("HIDController.linkControl - Field not found: " + group + "." + name); return; } - if (field.type === "bitvector") { - field = packet.lookupBit(group, name); + if (field.type !== "bitvector") { + field.mapped_group = m_group; + field.mapped_name = m_name; + if (callback !== undefined) { + field.callback = callback; + } + } else { + const bitField = packet.lookupBit(group, name); if (field === undefined) { console.error("HIDController.linkControl - Bit not found: " + group + "." + name); return; } - } - field.mapped_group = m_group; - field.mapped_name = m_name; - if (callback !== undefined) { - field.callback = callback; + bitField.mapped_group = m_group; + bitField.mapped_name = m_name; + if (callback !== undefined) { + bitField.callback = callback; + } } } /** From ea29630336e3e9f991e6a092161d00f66b010ddc Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Mon, 25 Jul 2022 21:41:13 +0200 Subject: [PATCH 44/60] Fixed eslint jsdoc/require-property-description warnings by documenting all class properties --- res/controllers/common-hid-packet-parser.js | 320 +++++++++++++++----- 1 file changed, 241 insertions(+), 79 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index f33bc20a807..0b8c608ac2f 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -93,6 +93,7 @@ this.HIDDebug = function(message) { * @property {string} name * @property {string} mapped_group * @property {string} mapped_name + * @property {controlCallback} mapped_callback * @property {number} bitmask * @property {number} bit_offset * @property {controlCallback} callback @@ -101,7 +102,7 @@ this.HIDDebug = function(message) { * @property {('button'|'output')} type Must be either: * - 'button' * - 'output' - * @property {boolean} value + * @property {number} value * @property {number} toggle */ @@ -111,15 +112,22 @@ this.HIDDebug = function(message) { * Collection of bits in one parsed packet field. These objects are * created by HIDPacket addControl and addOutput and should not be * created manually. - * - * @property {number} size - * @property {bitObject[]} bits */ // @ts-ignore Same identifier for class and instance needed for backward compatibility class HIDBitVector { constructor() { + /** + * Number of bitObjects in bits array + * + * @type {number} + */ this.size = 0; - this.bits = {}; + /** + * Array of bitObjects + * + * @type {bitObject[]} + */ + this.bits = new Array(); } /** * Get the index of the least significant bit that is 1 in `bitmask` @@ -210,13 +218,26 @@ this.HIDBitVector = HIDBitVector; /** * HID Modifiers object * + * e.g. a shift button can be defined as modifier for the behavior of other controls. + * * Wraps all defined modifiers to one object with uniform API. * Don't call directly, this is available as HIDController.modifiers */ // @ts-ignore Same identifier for class and instance needed for backward compatibility class HIDModifierList { constructor() { - this.modifiers = Object(); + /** + * Actual value of the modifier + * + * @type {boolean[]} + */ + this.modifiers = new Array(); + + /** + * Function to be called after modifier value changes + * + * @type {packetCallback[]} + */ this.callbacks = Object(); } /** @@ -235,7 +256,7 @@ class HIDModifierList { * Set modifier value * * @param {string} name Name of modifier - * @param {number|boolean} value Value to be set + * @param {boolean} value Value to be set */ set(name, value) { if (!(name in this.modifiers)) { @@ -252,12 +273,12 @@ class HIDModifierList { * Get modifier value * * @param {string} name Name of modifier - * @returns {number|boolean} Value of modifier + * @returns {boolean} Value of modifier */ get(name) { if (!(name in this.modifiers)) { console.error("HIDModifierList.get - Unknown modifier: " + name); - return false; + return undefined; } return this.modifiers[name]; } @@ -287,28 +308,57 @@ this.HIDModifierList = HIDModifierList; * currently not supported) * * Each HIDPacket must be registered to HIDController. - * - * @param {string} name Name of packet (it makes sense to refer the HID report type and HID - * Report-ID here e.g. 'InputReport_0x02' or 'OutputReport_0x81') - * @param {number} reportId ReportID of the packet. If the device does not use ReportIDs this must - * be 0. [default = 0] - * @param {packetCallback} callback function to call when the packet type represents an InputReport - * an a new report is received. If packet callback is set, the - * packet is not parsed by delta functions. - * callback is not meaningful for output packets - * @param {number[]} header (optional) list of bytes to match from beginning - * of packet. Do NOT put the report ID in this; use - * the reportId parameter instead. */ // @ts-ignore Same identifier for class and instance needed for backward compatibility class HIDPacket { - constructor(name, reportId = 0, callback, header) { + /** + * @param {string} name Name of packet (it makes sense to refer the HID report type and HID + * ReportID here e.g. 'InputReport_0x02' or 'OutputReport_0x81') + * @param {number} reportId ReportID of the packet. If the device does not use ReportIDs this + * must be 0. [default = 0] + * @param {packetCallback} callback function to call when the packet type represents an InputReport, + * and a new report is received. If packet callback is set, the packet is not parsed by delta + * functions. Note, that a callback is not meaningful for output packets. + * @param {number[]} header (optional) List of bytes to match from beginning of packet. + * Do NOT put the report ID in this - use the reportId parameter instead. + */ + constructor(name, reportId = 0, callback = undefined, header = []) { + /** + * Name of packet + * + * @type {string} + */ this.name = name; - this.header = header; - this.callback = callback; + + /** + * 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; + this.groups = {}; - this.length = 0; + + /** + * Length of packet in bytes + * + * @type {number} + */ + this.length = this.header.length; // Size of various 'pack' values in bytes this.packSizes = {b: 1, B: 1, h: 2, H: 2, i: 4, I: 4}; @@ -1049,50 +1099,45 @@ this.HIDPacket = HIDPacket; /** - * HID Controller Class - * - * HID Controller with packet parser - * Global attributes include: - * - * @property {boolean} initialized by default false, you should set this to true when - * controller is found and everything is OK - * @property {string} activeDeck by default undefined, used to map the virtual deck - * names 'deck','deck1' and 'deck2' to actual [ChannelX] - * @property {boolean} isScratchEnabled set to true, when button 'jog_touch' is active - * @property buttonStates valid state values for buttons, should contain fields - * released (default 0) and pressed (default 1) - * @property LEDColors possible Output colors named, must contain 'off' value - * @property deckOutputColors Which colors to use for each deck. Default 'on' for first - * four decks. Values are like {1: 'red', 2: 'green' } - * and must reference valid OutputColors fields. - * @property {number} OutputUpdateInterval By default undefined. If set, it's a value for timer - * executed every n ms to update Outputs with updateOutputs() - * @property {HIDModifierList} modifiers Reference to HIDModifierList object - * @property toggleButtons List of button names you wish to act as 'toggle', i.e. - * pressing the button and releasing toggles state of the - * control and does not set it off again when released. - * - * Scratch variables (initialized with 'common' defaults, you can override): - * @property {number} scratchintervalsPerRev Intervals value for scratch_enable - * @property {number} scratchRPM RPM value for scratch_enable - * @property {number} scratchAlpha Alpha value for scratch_enable - * @property {number} scratchBeta Beta value for scratch_enable - * @property {boolean} scratchRampOnEnable Set true to ramp the deck speed down. Set false to stop instantly [default = false] - * @property {boolean} scratchRampOnDisable Set true to ramp the deck speed up. Set false to jump to normal play speed instantly [default = false] - * @property {scratchingCallback} enableScratchCallback Callback function to call when, jog wheel scratching got enabled or disabled - * @property {number} auto_repeat_interval Auto repeat interval default for fields, where not - * specified individual + * HID Controller Class with packet parser */ // @ts-ignore Same identifier for class and instance needed for backward compatibility class HIDController { constructor() { + + /** + * - By default 'false' + * - Should be set 'true', when controller is found and everything is OK + * + * @type {boolean} + */ this.initialized = false; + + /** + * @type {number} + */ this.activeDeck = undefined; - this.InputPackets = {}; - this.OutputPackets = {}; - // Default input control packet name: can be modified for controllers - // which can swap modes (wiimote for example) + /** + * Array of HIDPackets representing HID InputReports + * + * @type {HIDPacket[]} + */ + this.InputPackets = new Array(); + + /** + * Array of HIDPackets representing HID OutputReports + * + * @type {HIDPacket[]} + */ + this.OutputPackets = new Array(); + + /** + * Default input packet name: can be modified for controllers + * which can swap modes (wiimote for example) + * + * @type {string} + */ this.defaultPacket = "control"; // Callback functions called by deck switching. Undefined by default @@ -1101,36 +1146,121 @@ class HIDController { // Scratch parameter defaults for this.scratchEnable function // override for custom control + /** + * Set to true, when button 'jog_touch' is active + * + * @type {boolean} + */ this.isScratchEnabled = false; + + /** + * The resolution of the jogwheel HID control (in intervals per revolution) + * - Default is 128 + * + * @type {number} + */ this.scratchintervalsPerRev = 128; + + /** + * 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; + + /** + * The alpha coefficient of the filter + * - Default is 1/8 (0.125) - start tune from there + * + * @type {number} + */ this.scratchAlpha = 1.0 / 8; + + /** + * The beta coefficient of the filter + * - Default is scratchAlpha/32 - start tune from there + * + * @type {number} + */ this.scratchBeta = this.scratchAlpha / 32; + + /** + * - Set 'true' to ramp the deck speed down. + * - Set 'false' to stop instantly (default) + * + * @type {boolean} + */ this.scratchRampOnEnable = false; + + /** + * - 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; - // Button states available + /** + * List of valid state values for buttons, should contain fields: + * - 'released' (default 0) + * - 'pressed' (default 1) + */ this.buttonStates = {released: 0, pressed: 1}; - // Output color values to send + + /** + * List of named output colors to send + * - must contain 'off' value + */ this.LEDColors = {off: 0x0, on: 0x7f}; - // Toggle buttons + + /** + * List of button names you wish to act as 'toggle' + * + * i.e. pressing the button and releasing toggles state of the control and doesn't set it off again when released. + */ this.toggleButtons = [ "play", "pfl", "keylock", "quantize", "reverse", "slip_enabled", "group_[Channel1]_enable", "group_[Channel2]_enable", "group_[Channel3]_enable", "group_[Channel4]_enable" ]; - // Override to set specific colors for multicolor button Output per deck + /** + * List of colors to use for each deck + * - Default is 'on' for first four decks. + * + * Override to set specific colors for multicolor button output per deck: + * - Values are like {1: 'red', 2: 'green' } and must reference valid OutputColors fields. + */ this.deckOutputColors = {1: "on", 2: "on", 3: "on", 4: "on"}; - // Mapping of automatic deck switching with deckSwitch function + + // + /** + * Used to map the virtual deck names 'deck', 'deck1' or 'deck2' to actual [ChannelX] + * + * @type {string[]} + */ this.virtualDecks = ["deck", "deck1", "deck2", "deck3", "deck4"]; + + /** + * Mapping of automatic deck switching with switchDeck function + */ this.deckSwitchMap = {1: 2, 2: 1, 3: 4, 4: 3, undefined: 1}; - // Standard target groups available in mixxx. This is used by - // HID packet parser to recognize group parameters we should - // try sending to mixxx. + /** + * 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 = [ "[Channel1]", "[Channel2]", @@ -1157,13 +1287,44 @@ class HIDController { "[InternalClock]" ]; - // Set to value in ms to update Outputs periodically + // + /** + * 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() + * + * @todo This is unused and updateOutputs() doesn't exist - Remove? + * @type number + */ this.OutputUpdateInterval = undefined; + /** + * Reference to HIDModifierList object + * + * @type {HIDModifierList} + */ this.modifiers = new HIDModifierList(); - this.scalers = {}; - this.timers = {}; + /** + * Array of scaling function callbacks + * + * @type {scalingCallback[]} + */ + this.scalers = new Array(); + + /** + * Array of timers + * + * @type {number[]} + */ + this.timers = new Array(); + + /** + * Auto repeat interval default for fields, where not specified individual (in milliseconds) + * - Default = 100 + * + * @type {number} + */ this.auto_repeat_interval = 100; } /** Function to close the controller object cleanly */ @@ -1406,7 +1567,7 @@ class HIDController { } bitField.group = "modifiers"; bitField.name = modifier; - this.modifiers.set(modifier); + this.modifiers.set(modifier, Boolean(bitField.value)); } /** * @todo Implement unlinking of modifiers @@ -1690,7 +1851,7 @@ class HIDController { } if (group === "modifiers") { - if (field.value !== false) { + if (field.value !== 0) { this.modifiers.set(control, true); } else { this.modifiers.set(control, false); @@ -1700,6 +1861,7 @@ class HIDController { if (field.auto_repeat) { const timer_id = "auto_repeat_" + field.id; if (field.value) { + // @ts-ignore startAutoRepeatTimer needs to be implemented in the users mapping this.startAutoRepeatTimer(timer_id, field.auto_repeat_interval); } else { this.stopAutoRepeatTimer(timer_id); @@ -1711,7 +1873,7 @@ class HIDController { } if (control === "jog_touch") { if (group !== undefined) { - if (field.value === Boolean(this.buttonStates.pressed)) { + if (field.value === this.buttonStates.pressed) { this.enableScratch(group, true); } else { this.enableScratch(group, false); @@ -1720,7 +1882,7 @@ class HIDController { return; } if (this.toggleButtons.indexOf(control) !== -1) { - if (field.value === Boolean(this.buttonStates.released)) { + if (field.value === this.buttonStates.released) { return; } if (engine.getValue(group, control)) { @@ -1734,7 +1896,7 @@ class HIDController { } return; } - if (field.auto_repeat && field.value === Boolean(this.buttonStates.pressed)) { + if (field.auto_repeat && field.value === this.buttonStates.pressed) { console.log("Callback for " + field.group); engine.setValue(group, control, field.auto_repeat(field)); } else if (engine.getValue(group, control) === 0) { @@ -1770,7 +1932,7 @@ class HIDController { return; } if (group === "modifiers") { - this.modifiers.set(control, Number(field.value)); + this.modifiers.set(control, Boolean(field.value)); return; } if (control === "jog_wheel") { From 22758571e8ffb6d373958555f63392de1b0beb2b Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Tue, 26 Jul 2022 10:21:22 +0200 Subject: [PATCH 45/60] Reverted wrong change of object literal to array, and documented the types accordingly --- res/controllers/common-hid-packet-parser.js | 68 +++++++++++++-------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 0b8c608ac2f..ac98fd1a9a4 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -18,7 +18,13 @@ this.HIDDebug = function(message) { * * @callback packetCallback * @param {HIDPacket} packet The packet that represents the InputReport - * @param {number[]} changed_data The data received from the device + * @param {Record.} 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. @@ -123,11 +129,11 @@ class HIDBitVector { */ this.size = 0; /** - * Array of bitObjects + * Object of bitObjects, referred by a string of group and control name separated by a dot * - * @type {bitObject[]} + * @type {Record.} */ - this.bits = new Array(); + this.bits = {}; } /** * Get the index of the least significant bit that is 1 in `bitmask` @@ -229,14 +235,14 @@ class HIDModifierList { /** * Actual value of the modifier * - * @type {boolean[]} + * @type {Record.} */ - this.modifiers = new Array(); + this.modifiers = Object(); /** * Function to be called after modifier value changes * - * @type {packetCallback[]} + * @type {Record.} */ this.callbacks = Object(); } @@ -286,7 +292,7 @@ class HIDModifierList { * Set modifier callback function * * @param {string} name Name of reference in HIDModifierList - * @param {packetCallback} callback Function to be called after modifier value changes + * @param {modifierCallback} callback Function to be called after modifier value changes */ setCallback(name, callback) { if (!(name in this.modifiers)) { @@ -967,11 +973,15 @@ class HIDPacket { * @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. - * @returns {bitObject[]} List of modified bits (delta) + * @returns {Record.} List of modified bits (delta) */ parseBitVector(field, value) { - /** @type {bitObject[]}*/ - const bits = new Array(); + /** + * Object of bitObjects, referred by a string of group and control name separated by a dot + * + * @type {Record.} + */ + const bits = {}; if (field.type !== "bitvector") { console.error("HIDPacket.parseBitVector - Field isn't of type bitvector"); @@ -994,11 +1004,15 @@ class HIDPacket { * BitVectors are returned as bits you can iterate separately. * * @param {Uint8Array} data Data received as InputReport from the device - * @returns {packetField[]|bitObject[]} List of changed fields with new value. + * @returns {Record.} List of changed fields with new value. */ parse(data) { - /** @type {packetField[]|bitObject[]}*/ - const field_changes = new Array(); + /** + * Object of packetField or bitObjects, referred by a string of group and control name separated by a dot + * + * @type {Record.} + */ + const field_changes = {}; for (const group_name in this.groups) { const group = this.groups[group_name]; @@ -1119,18 +1133,18 @@ class HIDController { this.activeDeck = undefined; /** - * Array of HIDPackets representing HID InputReports + * HIDPackets representing HID InputReports, by packet name * - * @type {HIDPacket[]} + * @type {Record.} */ - this.InputPackets = new Array(); + this.InputPackets = {}; /** - * Array of HIDPackets representing HID OutputReports + * HIDPackets representing HID OutputReports, by packet name * - * @type {HIDPacket[]} + * @type {Record.} */ - this.OutputPackets = new Array(); + this.OutputPackets = {}; /** * Default input packet name: can be modified for controllers @@ -1306,18 +1320,18 @@ class HIDController { this.modifiers = new HIDModifierList(); /** - * Array of scaling function callbacks + * Object of scaling function callbacks by name * - * @type {scalingCallback[]} + * @type {Record.} */ - this.scalers = new Array(); + this.scalers = {}; /** - * Array of timers + * Object of engine timer IDs, by hid-parser timer IDs * - * @type {number[]} + * @type {Record.} */ - this.timers = new Array(); + this.timers = {}; /** * Auto repeat interval default for fields, where not specified individual (in milliseconds) @@ -1774,7 +1788,7 @@ class HIDController { * fields in default mixxx groups. Not done if a callback was defined. * * @param packet Unused - * @param {packetField[]|bitObject[]} delta + * @param {Record.} delta */ processIncomingPacket(packet, delta) { /** @type {packetField} */ From 6d0d3286b315419b1814de47e6ffa224c63565a9 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Tue, 26 Jul 2022 11:12:43 +0200 Subject: [PATCH 46/60] Defined type alias packetItemId --- res/controllers/common-hid-packet-parser.js | 23 ++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index ac98fd1a9a4..97c243af7f5 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -51,6 +51,7 @@ this.HIDDebug = function(message) { * @param {number} value Value to be scaled * @returns {number} Scaled value */ + /** * Callback function to call when, jog wheel scratching got enabled or disabled by * the button with the special name 'jog_touch' @@ -58,11 +59,19 @@ this.HIDDebug = function(message) { * @callback scratchingCallback * @param {boolean} isScratchEnabled True, when button 'jog_touch' is active */ + +/** + * A string of group and control name separated by a dot + * + * @typedef packetItemId + * @type {string} + */ + /** * @typedef packetField * @type {object} * @property {HIDPacket} packet - * @property {string} id Group and control name separated by a dot + * @property {packetItemId} id Group and control name separated by a dot * @property {string} group * @property {string} name * @property {string} mapped_group @@ -94,7 +103,7 @@ this.HIDDebug = function(message) { * @typedef bitObject * @type {object} * @property {HIDPacket} packet - * @property {string} id Group and control name separated by a dot + * @property {packetItemId} id Group and control name separated by a dot * @property {string} group * @property {string} name * @property {string} mapped_group @@ -131,7 +140,7 @@ class HIDBitVector { /** * Object of bitObjects, referred by a string of group and control name separated by a dot * - * @type {Record.} + * @type {Record.} */ this.bits = {}; } @@ -973,13 +982,13 @@ class HIDPacket { * @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. - * @returns {Record.} List of modified bits (delta) + * @returns {Record.} List of modified bits (delta) */ parseBitVector(field, value) { /** * Object of bitObjects, referred by a string of group and control name separated by a dot * - * @type {Record.} + * @type {Record.} */ const bits = {}; @@ -1004,13 +1013,13 @@ class HIDPacket { * BitVectors are returned as bits you can iterate separately. * * @param {Uint8Array} data Data received as InputReport from the device - * @returns {Record.} List of changed fields with new value. + * @returns {Record.} 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 {Record.} + * @type {Record.} */ const field_changes = {}; From 095fe06783aebd00962dfa0b4039f349ae96005a Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Tue, 26 Jul 2022 19:34:48 +0200 Subject: [PATCH 47/60] Corrected definition of callback types --- res/controllers/common-hid-packet-parser.js | 92 +++++++++++++++------ 1 file changed, 66 insertions(+), 26 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 97c243af7f5..1bae3e43ff0 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -29,16 +29,30 @@ this.HIDDebug = function(message) { /** * Callback function to call when, data for specified filed in the packet is updated. * - * @callback controlCallback + * @callback fieldChangeCallback * @param {packetField|bitObject} Object that describes a field/bit inside of a packet, which can often * mapped to a Mixxx control. */ + +/** + * 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 + * @param {string} name Mixxx control name + * @returns {any} Value + */ + /** * In almost every case, a HID controller sends data values with input fields which are not directly * suitable for Mixxx control values. To solve this issue, HIDController contains function to scale * the input value to suitable range automatically before calling any field processing functions. - * Scalers can be registered with HIDController.registerScalingFunction(group,name,callback) in - * HIDController. + * Scalers can be registered with HIDController.setScaler. + * + * 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 Defines the group name for the field. The group can be any string, but if @@ -82,10 +96,10 @@ this.HIDDebug = function(message) { * @property {number} end_offset * @property {number} bitmask * @property {boolean} isEncoder - * @property {controlCallback} callback + * @property {fieldChangeCallback} callback * @property {boolean} soft_takeover * @property {boolean} ignored - * @property {controlCallback} auto_repeat + * @property {fieldChangeCallback} auto_repeat * @property {number} auto_repeat_interval * @property {number} min * @property {number} max @@ -111,8 +125,8 @@ this.HIDDebug = function(message) { * @property {controlCallback} mapped_callback * @property {number} bitmask * @property {number} bit_offset - * @property {controlCallback} callback - * @property {controlCallback} auto_repeat + * @property {fieldChangeCallback} callback + * @property {fieldChangeCallback} auto_repeat * @property {number} auto_repeat_interval * @property {('button'|'output')} type Must be either: * - 'button' @@ -665,7 +679,7 @@ class HIDPacket { * 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 * reported - * @param {controlCallback} 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); @@ -808,7 +822,7 @@ class HIDPacket { * - I unsigned integer * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are * considered. - * @param {controlCallback} [callback=undefined] Callback function for the control + * @param {fieldChangeCallback} [callback=undefined] Callback function for the control */ addOutput(group, name, offset, pack, bitmask, callback) { const control_group = this.getGroup(group, true); @@ -901,7 +915,7 @@ class HIDPacket { * but if it matches a valid Mixxx control name in the group defined for field, the system * attempts to attach it directly to the correct field. Together group and name form the ID * of the field (group.name) - * @param {controlCallback} callback Callback function for the control + * @param {fieldChangeCallback} callback Callback function for the control */ setCallback(group, name, callback) { const field = this.getField(group, name); @@ -1317,7 +1331,7 @@ class HIDController { * - If set, it's a value for timer executed every n ms to update Outputs with updateOutputs() * * @todo This is unused and updateOutputs() doesn't exist - Remove? - * @type number + * @type {number} */ this.OutputUpdateInterval = undefined; @@ -1349,6 +1363,23 @@ class HIDController { * @type {number} */ this.auto_repeat_interval = 100; + + /** + * Deprecated: Use postProcessDelta instead + * (not used in any official mapping) + * + * @type {packetCallback} + */ + this.processDelta = undefined; + + /** + * Callback that is executed after parsing incoming packet + * (see Traktor-Kontrol-F1-scripts.js for an example) + * + * @type {packetCallback} + */ + this.postProcessDelta = undefined; + } /** Function to close the controller object cleanly */ close() { @@ -1527,7 +1558,7 @@ class HIDController { * but if it matches a valid Mixxx control name in the group defined for field, the system * attempts to attach it directly to the correct field. Together group and name form the ID * of the field (group.name) - * @param {controlCallback} callback Callback function for the control + * @param {fieldChangeCallback} callback Callback function for the control */ setCallback(packet, group, name, callback) { const input_packet = this.getInputPacket(packet); @@ -1554,11 +1585,11 @@ class HIDController { * Lookup scaling function for control * * @param {string} name Reference of the scaling function in scalers list of HIDController - * @param _callback Unused - * @returns {scalingCallback} Scaling function. Returns undefined if function is not + * @param {any} _callback Unused + * @returns {scalingCallback} Scaling function. Returns undefined if function is not * registered. */ - getScaler(name, _callback) { + getScaler(name, _callback = undefined) { if (!(name in this.scalers)) { return undefined; } @@ -1574,7 +1605,7 @@ class HIDController { * but if it matches a valid Mixxx control name in the group defined for field, the system * attempts to attach it directly to the correct field. Together group and name form the ID * of the field (group.name) - * @param {any} modifier + * @param {string} modifier Name of the modifier e.g. 'shiftbutton' */ linkModifier(group, name, modifier) { const packet = this.getInputPacket(this.defaultPacket); @@ -1592,15 +1623,17 @@ class HIDController { bitField.name = modifier; this.modifiers.set(modifier, Boolean(bitField.value)); } + /** * @todo Implement unlinking of modifiers * @param {string} _group Unused * @param {string} _name Unused - * @param _modifier Unused + * @param {string} _modifier Unused */ unlinkModifier(_group, _name, _modifier) { console.warn("HIDController.unlinkModifier - Unlinking of modifiers not yet implemented"); } + /** * Link a previously declared HID control to actual mixxx control * @@ -1613,7 +1646,7 @@ class HIDController { * of the field (group.name) * @param {string} m_group Mapped group * @param {string} m_name Mapped name - * @param {controlCallback} callback Callback function for the control + * @param {fieldChangeCallback} callback Callback function for the control */ linkControl(group, name, m_group, m_name, callback) { const packet = this.getInputPacket(this.defaultPacket); @@ -1719,7 +1752,7 @@ 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 processDelta for results after processing automated fields + * - If defined, calls postProcessDelta for results after processing automated fields * * @param {Uint8Array} data The data received from an HID InputReport. * In case of HID devices, which use ReportIDs to enumerate the reports, @@ -1796,13 +1829,16 @@ class HIDController { * - Finally tries run matching engine.setValue() function for control * fields in default mixxx groups. Not done if a callback was defined. * - * @param packet Unused + * @param {any} packet Unused * @param {Record.} delta */ processIncomingPacket(packet, delta) { /** @type {packetField} */ for (const name in delta) { + // @ts-ignore ignoredControlChanges should be defined in the users mapping + // see EKS-Otus.js for an example if (this.ignoredControlChanges !== undefined && + // @ts-ignore this.ignoredControlChanges.indexOf(name) !== -1) { continue; } @@ -1992,8 +2028,8 @@ class HIDController { * @param {string} group Defines the group name for the field. The group can be any string, but * if it matches a valid Mixxx control group name, it is possible to map a field to a * control or output without any additional code. - * @param control - * @param value + * @param {string} control Name of the control (button) + * @param {number} value Value defined in this.buttonStates */ toggle(group, control, value) { if (value === this.buttonStates.released) { @@ -2083,11 +2119,15 @@ class HIDController { */ jog_wheel(field) { const active_group = this.getActiveFieldGroup(field); - let value = undefined; + if (field.type === "bitvector") { + console.error("HIDPacket.jog_wheel - Setting field.value of type for bitvector packet does not make sense"); + return; + } + let value; if (field.isEncoder) { - value = field.delta; + value = Number(field.delta); } else { - value = field.value; + value = Number(field.value); } if (this.isScratchEnabled) { const deck = this.resolveDeck(active_group); @@ -2132,7 +2172,7 @@ class HIDController { * * @param {string} group * @param {string} name - * @param {controlCallback} callback Callback function for the control + * @param {fieldChangeCallback} callback Callback function for the control * @param {number} interval */ setAutoRepeat(group, name, callback, interval) { From f69c587b1115b4517a9eeab5107c11a1b896aee2 Mon Sep 17 00:00:00 2001 From: JoergAtGithub Date: Wed, 27 Jul 2022 10:58:45 +0200 Subject: [PATCH 48/60] Shortened description of control group and control name arguments Made the difference between mapped control and control clear --- res/controllers/common-hid-packet-parser.js | 225 +++++--------------- 1 file changed, 58 insertions(+), 167 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 1bae3e43ff0..dee4d174881 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -39,8 +39,8 @@ this.HIDDebug = function(message) { * * @callback controlCallback * @param {number} value New value of the control - * @param {string} group Mixxx control group name - * @param {string} name Mixxx control name + * @param {string} group Mixxx control group name e.g. "[Channel1]" + * @param {string} name Mixxx control name "pregain" * @returns {any} Value */ @@ -55,13 +55,8 @@ this.HIDDebug = function(message) { * - 'true' engine.setParameter is used * * @callback scalingCallback - * @param {string} group Defines the group name for the field. The group can be any string, but if - * it matches a valid Mixxx control group name, it is possible to map a field to a control or - * output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, but if - * it matches a valid Mixxx control name in the group defined for field, the system attempts to - * attach it directly to the correct field. Together group and name form the ID of the field - * (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "pregain" * @param {number} value Value to be scaled * @returns {number} Scaled value */ @@ -88,8 +83,8 @@ this.HIDDebug = function(message) { * @property {packetItemId} id Group and control name separated by a dot * @property {string} group * @property {string} name - * @property {string} mapped_group - * @property {string} mapped_name + * @property {string} mapped_group Mapped group, must be a valid Mixxx control group name e.g. "[Channel1]" + * @property {string} mapped_name Name of mapped control, must be a valid Mixxx control name "VuMeter" * @property {controlCallback} mapped_callback * @property {object} pack Control packing format for unpack(), one of b/B, h/H, i/I * @property {number} offset @@ -120,8 +115,8 @@ this.HIDDebug = function(message) { * @property {packetItemId} id Group and control name separated by a dot * @property {string} group * @property {string} name - * @property {string} mapped_group - * @property {string} mapped_name + * @property {string} mapped_group Mapped group, must be a valid Mixxx control group name e.g. "[Channel1]" + * @property {string} mapped_name Name of mapped control, must be a valid Mixxx control name "cue_indicator" * @property {controlCallback} mapped_callback * @property {number} bitmask * @property {number} bit_offset @@ -178,15 +173,8 @@ class HIDBitVector { /** * Add a control bitmask to the HIDBitVector * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control without any additional code. Defines the group name for the field. The group can - * be any string, but if it matches a valid Mixxx control group name, it is possible to map - * a field to a control or output without any additional code - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "play" * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are * considered. */ @@ -211,13 +199,8 @@ class HIDBitVector { /** * Add an output control bitmask to the HIDBitVector * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to an - * output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "play" * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are * considered. */ @@ -559,13 +542,8 @@ class HIDPacket { * Return a field by group and name from the packet, * Returns undefined if field could not be found * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "play" * @returns {packetField} Field */ getField(group, name) { @@ -602,13 +580,8 @@ class HIDPacket { /** * Return reference to a bit in a bitvector field * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "play" * @returns {bitObject} Reference to a bit in a bitvector field */ lookupBit(group, name) { @@ -635,13 +608,8 @@ class HIDPacket { /** * Remove a control registered. Normally not needed * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "play" */ removeControl(group, name) { const control_group = this.getGroup(group); @@ -654,13 +622,11 @@ class HIDPacket { /** * Register a numeric value to parse from input packet * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. control group name - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * '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 "play" * @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 @@ -801,13 +767,8 @@ class HIDPacket { * 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 Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @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: * - For HID devices which don't use ReportIDs, the data bytes starts at * position 0 @@ -908,13 +869,8 @@ 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 Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "play" * @param {fieldChangeCallback} callback Callback function for the control */ setCallback(group, name, callback) { @@ -948,13 +904,8 @@ class HIDPacket { * 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 Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @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) */ setIgnored(group, name, ignored) { @@ -969,13 +920,8 @@ class HIDPacket { * Adjust field's minimum delta value. * Input value changes smaller than this are not reported in delta * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "pregain" * @param {number} mindelta Minimum delta value. */ setMinDelta(group, name, mindelta) { @@ -1401,9 +1347,7 @@ class HIDController { * Return deck number from deck name. Deck name can't be virtual deck name * in this function call. * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. + * @param {string} group Control group name e.g. "[Channel1]" * @returns {number} Number of deck */ resolveDeck(group) { @@ -1433,9 +1377,7 @@ 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 Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. + * @param {string} group Control group name e.g. "[Channel1]" * @returns {string} Channel */ resolveGroup(group) { @@ -1467,13 +1409,8 @@ class HIDController { * * @todo The current implementation of this often called function is very slow and does not * scale, due to several nested loops. - * @param {string} m_group Defines the group name for the field. The group can be any string, - * but if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} m_name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.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 * can't be found. */ @@ -1551,13 +1488,8 @@ class HIDController { * 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 Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "pregain" * @param {fieldChangeCallback} callback Callback function for the control */ setCallback(packet, group, name, callback) { @@ -1598,13 +1530,8 @@ class HIDController { /** * Change type of a previously defined field to modifier and register it * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name * @param {string} modifier Name of the modifier e.g. 'shiftbutton' */ linkModifier(group, name, modifier) { @@ -1637,15 +1564,10 @@ class HIDController { /** * Link a previously declared HID control to actual mixxx control * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) - * @param {string} m_group Mapped group - * @param {string} m_name Mapped name + * @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]" + * @param {string} m_name Name of mapped control, must be a valid Mixxx control name "pregain" * @param {fieldChangeCallback} callback Callback function for the control */ linkControl(group, name, m_group, m_name, callback) { @@ -1680,13 +1602,8 @@ class HIDController { } /** * @todo Implement unlinking of controls - * @param {string} _group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} _name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} _group Mixxx control group name e.g. "[Channel1]" + * @param {string} _name Mixxx control name "pregain" */ unlinkControl(_group, _name) {} /** @@ -2025,9 +1942,7 @@ class HIDController { /** * Toggle control state from toggle button * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. + * @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 */ @@ -2041,9 +1956,7 @@ class HIDController { /** * Toggle play/pause state * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. + * @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. */ @@ -2063,9 +1976,7 @@ class HIDController { * when scratching should be enabled. * Deck is resolved from group with 'resolveDeck' * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. + * @param {string} group Control group name e.g. "[Channel1]" * @param {boolean} status Enable or Disable scratching: * - true enables scratching (press 'jog_touch' button) * Sets the internal 'isScratchEnabled' attribute to true, and calls scratchEnable @@ -2305,15 +2216,10 @@ class HIDController { /** * Link a virtual HID Output to mixxx control * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) - * @param {string} m_group Mapped group - * @param {string} m_name Name of mapped 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]" + * @param {string} m_name Name of mapped control, must be a valid Mixxx control name "VuMeter" * @param {controlCallback} callback Callback function for the control */ linkOutput(group, name, m_group, m_name, callback) { @@ -2340,13 +2246,8 @@ class HIDController { /** * Unlink a virtual HID Output from mixxx control * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @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 */ unlinkOutput(group, name, callback) { @@ -2368,13 +2269,8 @@ class HIDController { /** * Set output state to given value * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @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 * immediately @@ -2394,13 +2290,8 @@ class HIDController { /** * Set Output to toggle between two values. Reset with setOutput(name,'off') * - * @param {string} group Defines the group name for the field. The group can be any string, but - * if it matches a valid Mixxx control group name, it is possible to map a field to a - * control or output without any additional code. - * @param {string} name Is the name of the control for the field. The name can be any string, - * but if it matches a valid Mixxx control name in the group defined for field, the system - * attempts to attach it directly to the correct field. Together group and name form the ID - * of the field (group.name) + * @param {string} group Control group name e.g. "[Channel1]" + * @param {string} name Control name "cue_indicator" * @param toggle_value */ setOutputToggle(group, name, toggle_value) { From af39091d76c322f425a26aedc7f130a12cbd70ba Mon Sep 17 00:00:00 2001 From: Joerg Date: Sun, 9 Oct 2022 14:48:48 +0200 Subject: [PATCH 49/60] Replaced "Record." by "Object.", because Record is TypeScript syntax and not valid JSDoc. -- Unfortunately I had to replace the typedef packetItemId, because the JSDoc Object. syntax for records requires type string as index. -- For type compatibility with TypeScript (and TypeScript based tools like VS Code), the keyword Object in JSDoc must be upper case. To prevent such tools reporting false positive errors, the setting of eslint-plugin-jsdoc (this case is described here: https://github.com/gajus/eslint-plugin-jsdoc/blob/master/README.md#why-not-capital-case-everything) --- .eslintrc.json | 80 ++++++++++++++------- res/controllers/common-hid-packet-parser.js | 50 ++++++------- 2 files changed, 76 insertions(+), 54 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index f264b0ea417..784a383b209 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,5 +1,5 @@ { - "extends": ["eslint:recommended", "plugin:jsdoc/recommended"], + "extends": [ "eslint:recommended", "plugin:jsdoc/recommended" ], "parserOptions": { "ecmaVersion": 7, "sourceType": "script" @@ -7,30 +7,49 @@ "env": { "es6": true }, - "plugins": ["jsdoc"], + "plugins": [ "jsdoc" ], + "settings": { + "jsdoc": { + "preferredTypes": { + "object": "Object" + } + } + }, "rules": { - "array-bracket-spacing" : "warn", + "array-bracket-spacing": "warn", "block-spacing": "warn", - "brace-style": ["warn", "1tbs", { + "brace-style": [ + "warn", + "1tbs", + { "allowSingleLine": true - }], + } + ], "curly": "warn", "camelcase": "warn", "comma-spacing": "warn", - "computed-property-spacing" : ["warn", "never", { + "computed-property-spacing": [ + "warn", + "never", + { "enforceForClassMembers": true - }], - "dot-location": ["warn", "property"], + } + ], + "dot-location": [ "warn", "property" ], "dot-notation": "warn", - "eqeqeq": ["error", "always"], + "eqeqeq": [ "error", "always" ], "func-call-spacing": "warn", - "func-style": ["error", "expression", { + "func-style": [ + "error", + "expression", + { "allowArrowFunctions": true - }], - "indent": ["warn", 4], + } + ], + "indent": [ "warn", 4 ], "key-spacing": "warn", "keyword-spacing": "warn", - "linebreak-style": ["warn", "unix"], + "linebreak-style": [ "warn", "unix" ], "newline-per-chained-call": "warn", "no-constructor-return": "warn", "no-extra-bind": "warn", @@ -38,26 +57,35 @@ "no-useless-call": "warn", "no-useless-return": "warn", "no-trailing-spaces": "warn", - "no-unneeded-ternary": ["warn", { + "no-unneeded-ternary": [ + "warn", + { "defaultAssignment": false - }], - "no-unused-vars": ["error", { + } + ], + "no-unused-vars": [ + "error", + { "argsIgnorePattern": "^_" - }], + } + ], "no-var": "warn", - "object-curly-newline" : ["warn", { + "object-curly-newline": [ + "warn", + { "consistent": true, "multiline": true - }], - "object-curly-spacing" : "warn", + } + ], + "object-curly-spacing": "warn", "prefer-const": "warn", "prefer-regex-literals": "warn", - "quotes": ["warn", "double"], + "quotes": [ "warn", "double" ], "require-atomic-updates": "error", "semi": "warn", "semi-spacing": "warn", - "space-before-blocks": ["warn", "always"], - "space-before-function-paren": ["warn", "never"], + "space-before-blocks": [ "warn", "always" ], + "space-before-function-paren": [ "warn", "never" ], "space-in-parens": "warn", "yoda": "warn" }, @@ -66,13 +94,13 @@ }, "overrides": [ { - "files": ["**/*.mjs"], + "files": [ "**/*.mjs" ], "parserOptions": { "sourceType": "module" } }, { - "files": ["res/controllers/common-hid-packet-parser.js"], + "files": [ "res/controllers/common-hid-packet-parser.js" ], "globals": { "console": "readonly" }, @@ -81,7 +109,7 @@ } }, { - "files": ["res/controllers/*.js"], + "files": [ "res/controllers/*.js" ], "excludedFiles": "res/controllers/common-hid-packet-parser.js", "globals": { "console": "readonly", diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index dee4d174881..0f97b1ac2a6 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -18,7 +18,7 @@ this.HIDDebug = function(message) { * * @callback packetCallback * @param {HIDPacket} packet The packet that represents the InputReport - * @param {Record.} changed_data The data received from the device + * @param {Object.} changed_data The data received from the device */ /** * Callback function to call when, the value of a modifier control changed @@ -69,24 +69,17 @@ this.HIDDebug = function(message) { * @param {boolean} isScratchEnabled True, when button 'jog_touch' is active */ -/** - * A string of group and control name separated by a dot - * - * @typedef packetItemId - * @type {string} - */ - /** * @typedef packetField - * @type {object} + * @type {Object} * @property {HIDPacket} packet - * @property {packetItemId} id Group and control name separated by a dot + * @property {string} id Group and control name separated by a dot * @property {string} group * @property {string} name * @property {string} mapped_group Mapped group, must be a valid Mixxx control group name e.g. "[Channel1]" * @property {string} mapped_name Name of mapped control, must be a valid Mixxx control name "VuMeter" * @property {controlCallback} mapped_callback - * @property {object} pack Control packing format for unpack(), one of b/B, h/H, i/I + * @property {Object} pack Control packing format for unpack(), one of b/B, h/H, i/I * @property {number} offset * @property {number} end_offset * @property {number} bitmask @@ -110,9 +103,9 @@ this.HIDDebug = function(message) { /** * @typedef bitObject - * @type {object} + * @type {Object} * @property {HIDPacket} packet - * @property {packetItemId} id Group and control name separated by a dot + * @property {string} id Group and control name separated by a dot * @property {string} group * @property {string} name * @property {string} mapped_group Mapped group, must be a valid Mixxx control group name e.g. "[Channel1]" @@ -149,7 +142,7 @@ class HIDBitVector { /** * Object of bitObjects, referred by a string of group and control name separated by a dot * - * @type {Record.} + * @type {Object.} */ this.bits = {}; } @@ -241,14 +234,14 @@ class HIDModifierList { /** * Actual value of the modifier * - * @type {Record.} + * @type {Object.} */ this.modifiers = Object(); /** * Function to be called after modifier value changes * - * @type {Record.} + * @type {Object.} */ this.callbacks = Object(); } @@ -497,7 +490,7 @@ class HIDPacket { * position 0 * - For HID devices which use ReportIDs to enumerate the reports, the * data bytes starts at position 1 - * @param {object} pack Is one of the field packing types: + * @param {Object} pack Is one of the field packing types: * - b signed byte * - B unsigned byte * - h signed short @@ -632,7 +625,7 @@ class HIDPacket { * position 0 * - For HID devices which use ReportIDs to enumerate the reports, the * data bytes starts at position 1 - * @param {object} pack Is one of the field packing types: + * @param {Object} pack Is one of the field packing types: * - b signed byte * - B unsigned byte * - h signed short @@ -774,7 +767,7 @@ class HIDPacket { * position 0 * - For HID devices which use ReportIDs to enumerate the reports, the * data bytes starts at position 1 - * @param {object} pack Is one of the field packing types: + * @param {Object} pack Is one of the field packing types: * - b signed byte * - B unsigned byte * - h signed short @@ -942,13 +935,14 @@ class HIDPacket { * @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. - * @returns {Record.} List of modified bits (delta) + * @returns {Object.} List of modified bits (delta), + * referred by a string of group and control name separated by a dot */ parseBitVector(field, value) { /** * Object of bitObjects, referred by a string of group and control name separated by a dot * - * @type {Record.} + * @type {Object.} */ const bits = {}; @@ -973,13 +967,13 @@ class HIDPacket { * BitVectors are returned as bits you can iterate separately. * * @param {Uint8Array} data Data received as InputReport from the device - * @returns {Record.} List of changed fields with new value. + * @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 {Record.} + * @type {Object.} */ const field_changes = {}; @@ -1104,14 +1098,14 @@ class HIDController { /** * HIDPackets representing HID InputReports, by packet name * - * @type {Record.} + * @type {Object.} */ this.InputPackets = {}; /** * HIDPackets representing HID OutputReports, by packet name * - * @type {Record.} + * @type {Object.} */ this.OutputPackets = {}; @@ -1291,14 +1285,14 @@ class HIDController { /** * Object of scaling function callbacks by name * - * @type {Record.} + * @type {Object.} */ this.scalers = {}; /** * Object of engine timer IDs, by hid-parser timer IDs * - * @type {Record.} + * @type {Object.} */ this.timers = {}; @@ -1747,7 +1741,7 @@ class HIDController { * fields in default mixxx groups. Not done if a callback was defined. * * @param {any} packet Unused - * @param {Record.} delta + * @param {Object.} delta */ processIncomingPacket(packet, delta) { /** @type {packetField} */ From 64251b255de6642fd028d8fd7ec4b8a42b70611e Mon Sep 17 00:00:00 2001 From: Joerg Date: Sun, 9 Oct 2022 16:48:33 +0200 Subject: [PATCH 50/60] Clarify, that "play" is just an example --- res/controllers/common-hid-packet-parser.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 0f97b1ac2a6..d3522bfab5d 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -167,7 +167,7 @@ class HIDBitVector { * Add a control bitmask to the HIDBitVector * * @param {string} group Control group name e.g. "[Channel1]" - * @param {string} name Control name "play" + * @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 * considered. */ @@ -193,7 +193,7 @@ class HIDBitVector { * Add an output control bitmask to the HIDBitVector * * @param {string} group Control group name e.g. "[Channel1]" - * @param {string} name Control name "play" + * @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 * considered. */ @@ -536,7 +536,7 @@ class HIDPacket { * Returns undefined if field could not be found * * @param {string} group Control group name e.g. "[Channel1]" - * @param {string} name Control name "play" + * @param {string} name Control name e.g. "play" * @returns {packetField} Field */ getField(group, name) { @@ -574,7 +574,7 @@ 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 "play" + * @param {string} name Control name e.g. "play" * @returns {bitObject} Reference to a bit in a bitvector field */ lookupBit(group, name) { @@ -602,7 +602,7 @@ class HIDPacket { * Remove a control registered. Normally not needed * * @param {string} group Control group name e.g. "[Channel1]" - * @param {string} name Control name "play" + * @param {string} name Control name e.g. "play" */ removeControl(group, name) { const control_group = this.getGroup(group); @@ -619,7 +619,7 @@ class HIDPacket { * 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 "play" + * @param {string} name Control name e.g. "play" * @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 @@ -863,7 +863,7 @@ class HIDPacket { * 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 "play" + * @param {string} name Control name e.g. "play" * @param {fieldChangeCallback} callback Callback function for the control */ setCallback(group, name, callback) { From 9a872e61382ab6864d8d5c7f45082a951f7f3617 Mon Sep 17 00:00:00 2001 From: Joerg Date: Sun, 9 Oct 2022 19:09:21 +0200 Subject: [PATCH 51/60] Use JSDoc statements for deprecated info of processDelta --- res/controllers/common-hid-packet-parser.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index d3522bfab5d..1aa051eb504 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1305,9 +1305,8 @@ class HIDController { this.auto_repeat_interval = 100; /** - * Deprecated: Use postProcessDelta instead + * @deprecated Use {@link postProcessDelta} instead * (not used in any official mapping) - * * @type {packetCallback} */ this.processDelta = undefined; From 651089a5c59f3ae94272a82728004722b97e9a84 Mon Sep 17 00:00:00 2001 From: Joerg Date: Sun, 9 Oct 2022 21:14:40 +0200 Subject: [PATCH 52/60] Deprecated unused HIDController.OutputUpdateInterval --- res/controllers/common-hid-packet-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 1aa051eb504..7fc96b3e373 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1270,7 +1270,7 @@ class HIDController { * - By default undefined. * - If set, it's a value for timer executed every n ms to update Outputs with updateOutputs() * - * @todo This is unused and updateOutputs() doesn't exist - Remove? + * @deprecated This is unused and updateOutputs() doesn't exist - Remove? * @type {number} */ this.OutputUpdateInterval = undefined; From 96f105265b9261bc4f110414be1c1f65d10e0101 Mon Sep 17 00:00:00 2001 From: Joerg Date: Mon, 10 Oct 2022 22:29:30 +0200 Subject: [PATCH 53/60] Used @default syntax - According the JSDoc spec, the value is optional, I added it nevertheless, because VS Code doesn't print it otherwise --- res/controllers/common-hid-packet-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 7fc96b3e373..9caffe24053 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1298,8 +1298,8 @@ class HIDController { /** * Auto repeat interval default for fields, where not specified individual (in milliseconds) - * - Default = 100 * + * @default 100 * @type {number} */ this.auto_repeat_interval = 100; From f54a65a1eb451fa28f3c9611094dcd0d2b116403 Mon Sep 17 00:00:00 2001 From: Joerg Date: Mon, 10 Oct 2022 23:43:12 +0200 Subject: [PATCH 54/60] Replaced all strings with embedded expressions by template strings --- res/controllers/common-hid-packet-parser.js | 146 ++++++++++---------- 1 file changed, 70 insertions(+), 76 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 9caffe24053..a496bdf725b 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -7,7 +7,7 @@ * @param {any} message Message to be printed on controller debug console output */ this.HIDDebug = function(message) { - console.log("HID " + message); + console.log(`HID ${message}`); }; /** @@ -176,7 +176,7 @@ class HIDBitVector { const bit = {}; bit.type = "button"; bit.packet = undefined; - bit.id = group + "." + name; + bit.id = `${group}.${name}`; bit.group = group; bit.name = name; bit.mapped_group = undefined; @@ -202,7 +202,7 @@ class HIDBitVector { const bit = {}; bit.type = "output"; bit.packet = undefined; - bit.id = group + "." + name; + bit.id = `${group}.${name}`; bit.group = group; bit.name = name; bit.mapped_group = undefined; @@ -252,7 +252,7 @@ class HIDModifierList { */ add(name) { if (name in this.modifiers) { - console.warn("HIDModifierList.add - Modifier already defined: " + name); + console.warn(`HIDModifierList.add - Modifier already defined: ${name}`); return; } this.modifiers[name] = undefined; @@ -265,7 +265,7 @@ class HIDModifierList { */ set(name, value) { if (!(name in this.modifiers)) { - console.error("HIDModifierList.set - Unknown modifier: " + name); + console.error(`HIDModifierList.set - Unknown modifier: ${name}`); return; } this.modifiers[name] = value; @@ -282,7 +282,7 @@ class HIDModifierList { */ get(name) { if (!(name in this.modifiers)) { - console.error("HIDModifierList.get - Unknown modifier: " + name); + console.error(`HIDModifierList.get - Unknown modifier: ${name}`); return undefined; } return this.modifiers[name]; @@ -295,7 +295,7 @@ class HIDModifierList { */ setCallback(name, callback) { if (!(name in this.modifiers)) { - console.error("HIDModifierList.setCallback - Unknown modifier: " + name); + console.error(`HIDModifierList.setCallback - Unknown modifier: ${name}`); return; } this.callbacks[name] = callback; @@ -381,7 +381,7 @@ class HIDPacket { */ pack(data, field) { if (!(field.pack in this.packSizes)) { - console.error("HIDPacket.pack - Parsing packed value: invalid pack format " + field.pack); + console.error(`HIDPacket.pack - Parsing packed value: invalid pack format ${field.pack}`); return; } const bytes = this.packSizes[field.pack]; @@ -402,7 +402,7 @@ class HIDPacket { const value = Number((field.value !== undefined) ? field.value : 0); if (value < field.min || value > field.max) { - console.error("HIDPacket.pack - " + field.id + " packed value out of range: " + value); + console.error(`HIDPacket.pack - ${field.id} packed value out of range: ${value}`); return; } @@ -437,7 +437,7 @@ class HIDPacket { unpack(data, field) { if (!(field.pack in this.packSizes)) { - console.error("HIDPacket.unpack - Parsing packed value: invalid pack format " + field.pack); + console.error(`HIDPacket.unpack - Parsing packed value: invalid pack format ${field.pack}`); return undefined; } const bytes = this.packSizes[field.pack]; @@ -501,7 +501,7 @@ class HIDPacket { */ getFieldByOffset(offset, pack) { if (!(pack in this.packSizes)) { - console.error("HIDPacket.getFieldByOffset - Unknown pack string " + pack); + console.error(`HIDPacket.getFieldByOffset - Unknown pack string ${pack}`); return undefined; } const end_offset = offset + this.packSizes[pack]; @@ -540,9 +540,9 @@ class HIDPacket { * @returns {packetField} Field */ getField(group, name) { - const field_id = group + "." + name; + const field_id = `${group}.${name}`; if (!(group in this.groups)) { - console.error("HIDPacket.getField - Packet " + this.name + " group not found " + group); + console.error(`HIDPacket.getField - Packet ${this.name} group not found ${group}`); return undefined; } @@ -580,15 +580,15 @@ class HIDPacket { lookupBit(group, name) { const field = this.getField(group, name); if (field === undefined) { - console.error("HIDPacket.lookupBit - Bitvector match not found: " + group + "." + name); + console.error(`HIDPacket.lookupBit - Bitvector match not found: ${group}.${name}`); return undefined; } if (field.type !== "bitvector") { - console.error("HIDPacket.lookupBit - Control doesn't refer a field of type bitvector: " + group + "." + name); + console.error(`HIDPacket.lookupBit - Control doesn't refer a field of type bitvector: ${group}.${name}`); return undefined; } const bitVector = /** @type {HIDBitVector} */ (field.value); - const bit_id = group + "." + name; + const bit_id = `${group}.${name}`; for (const bit_name in bitVector.bits) { const bit = bitVector.bits[bit_name]; if (bit.id === bit_id) { @@ -607,7 +607,7 @@ class HIDPacket { removeControl(group, name) { const control_group = this.getGroup(group); if (!(name in control_group)) { - console.warn("HIDPacket.removeControl - Field not in control group " + group + ": " + name); + console.warn(`HIDPacket.removeControl - Field not in control group ${group}: ${name}`); return; } delete control_group[name]; @@ -643,23 +643,23 @@ class HIDPacket { addControl(group, name, offset, pack, bitmask, isEncoder, callback) { const control_group = this.getGroup(group, true); if (control_group === undefined) { - console.error("HIDPacket.addControl - Creating HID packet group " + group); + console.error(`HIDPacket.addControl - Creating HID packet group ${group}`); return; } if (!(pack in this.packSizes)) { - console.error("HIDPacket.addControl - Unknown pack value " + pack); + console.error(`HIDPacket.addControl - Unknown pack value ${pack}`); return; } const fieldByOffset = this.getFieldByOffset(offset, pack); if (fieldByOffset !== undefined) { if (bitmask === undefined) { - console.error("HIDPacket.addControl - Registering offset " + offset + " pack " + pack); - console.error("HIDPacket.addControl - Trying to overwrite non-bitmask control " + group + " " + name); + console.error(`HIDPacket.addControl - Registering offset ${offset} pack ${pack}`); + console.error(`HIDPacket.addControl - Trying to overwrite non-bitmask control ${group} ${name}`); return; } if (fieldByOffset.type !== "bitvector") { - console.error("HIDPacket.addControl - Field is not of type bitvector: " + group + "." + name); + console.error(`HIDPacket.addControl - Field is not of type bitvector: ${group}.${name}`); return; } else { const bitVector = /** @type {HIDBitVector} */ (fieldByOffset.value); @@ -667,8 +667,7 @@ class HIDPacket { if (callback !== undefined) { if (typeof callback !== "function") { console.error( - "HIDPacket.addControl - Callback provided for " + group + "." + name + - " is not a function."); + `HIDPacket.addControl - Callback provided for ${group}.${name} is not a function.`); return; } this.setCallback(group, name, callback); @@ -680,7 +679,7 @@ class HIDPacket { /** @type {packetField} */ const field = {}; field.packet = undefined; - field.id = group + "." + name; + field.id = `${group}.${name}`; field.group = group; field.name = name; field.mapped_group = undefined; @@ -724,10 +723,10 @@ class HIDPacket { } // Create a new bitvector field and add the bit to that // TODO - accept controls with bitmask < packet_max_value - const field_name = "bitvector_" + offset; + const field_name = `bitvector_${offset}`; field.type = "bitvector"; field.name = field_name; - field.id = group + "." + field_name; + field.id = `${group}.${field_name}`; const bitvector = new HIDBitVector(); bitvector.size = field.max; bitvector.addBitMask(group, name, bitmask); @@ -743,8 +742,7 @@ class HIDPacket { if (callback !== undefined) { if (typeof callback !== "function") { console.error( - "HIDPacket.addControl - Callback provided for " + group + "." + name + - " is not a function."); + `HIDPacket.addControl - Callback provided for ${group}.${name} is not a function.`); return; } this.setCallback(group, name, callback); @@ -780,13 +778,13 @@ class HIDPacket { */ addOutput(group, name, offset, pack, bitmask, callback) { const control_group = this.getGroup(group, true); - const field_id = group + "." + name; + const field_id = `${group}.${name}`; if (control_group === undefined) { return; } if (!(pack in this.packSizes)) { - console.error("HIDPacket.addOutput - Unknown Output control pack value " + pack); + console.error(`HIDPacket.addOutput - Unknown Output control pack value ${pack}`); return; } @@ -798,11 +796,11 @@ class HIDPacket { const fieldByOffset = this.getFieldByOffset(offset, pack); if (fieldByOffset !== undefined) { if (bitmask === undefined) { - console.error("HIDPacket.addOutput - Overwrite non-bitmask control " + group + "." + name); + console.error(`HIDPacket.addOutput - Overwrite non-bitmask control ${group}.${name}`); return; } if (fieldByOffset.type !== "bitvector") { - console.error("HIDPacket.addOutput - Field is not of type bitvector: " + group + "." + name); + console.error(`HIDPacket.addOutput - Field is not of type bitvector: ${group}.${name}`); return; } const bitVector = /** @type {HIDBitVector} */ (fieldByOffset.value); @@ -842,9 +840,9 @@ class HIDPacket { } else { // Create new Output bitvector control field, add bit to it // rewrite name to use bitvector instead - const field_name = "bitvector_" + offset; + const field_name = `bitvector_${offset}`; field.type = "bitvector"; - field.id = group + "." + field_name; + field.id = `${group}.${field_name}`; field.name = field_name; const bitvector = new HIDBitVector(); bitvector.size = field.max; @@ -868,13 +866,13 @@ class HIDPacket { */ setCallback(group, name, callback) { const field = this.getField(group, name); - const field_id = group + "." + name; + const field_id = `${group}.${name}`; if (callback === undefined) { - console.error("HIDPacket.setCallback - Callback to add was undefined for " + field_id); + console.error(`HIDPacket.setCallback - Callback to add was undefined for ${field_id}`); return; } if (field === undefined) { - console.error("HIDPacket.setCallback - Field for " + field_id + " not found"); + console.error(`HIDPacket.setCallback - Field for ${field_id} not found`); return; } if (field.type === "bitvector") { @@ -887,7 +885,7 @@ class HIDPacket { bit.callback = callback; return; } - console.error("HIDPacket.setCallback - Bit not found " + field_id); + console.error(`HIDPacket.setCallback - Bit not found ${field_id}`); } else { field.callback = callback; } @@ -904,7 +902,7 @@ class HIDPacket { setIgnored(group, name, ignored) { const field = this.getField(group, name); if (field === undefined) { - console.error("HIDPacket.setIgnored - Setting ignored flag for " + group + " " + name); + console.error(`HIDPacket.setIgnored - Setting ignored flag for ${group} ${name}`); return; } field.ignored = ignored; @@ -920,7 +918,7 @@ class HIDPacket { setMinDelta(group, name, mindelta) { const field = this.getField(group, name); if (field === undefined) { - console.error("HIDPacket.setMinDelta - Adjusting mindelta for " + group + " " + name); + console.error(`HIDPacket.setMinDelta - Adjusting mindelta for ${group} ${name}`); return; } if (field.type === "bitvector") { @@ -987,7 +985,7 @@ class HIDPacket { const value = this.unpack(data, field); if (value === undefined) { - console.error("HIDPacket.parse - Parsing packet field value for " + field_id); + console.error(`HIDPacket.parse - Parsing packet field value for ${field_id}`); return; } @@ -1065,7 +1063,7 @@ class HIDPacket { } packet_string += data[d].toString(16) + " "; } - console.log("Sending packet with Report ID " + this.reportId + ": " + packet_string); + console.log(`Sending packet with Report ID ${this.reportId}: ${packet_string}`); } controller.sendOutputReport(this.reportId, data.buffer); } @@ -1364,7 +1362,7 @@ class HIDController { if (deck === undefined) { return undefined; } - return "[Channel" + deck + "]"; + return `[Channel${deck}]`; } /** * Map virtual deck names to real deck group. If group is already @@ -1385,7 +1383,7 @@ class HIDController { if (this.activeDeck === undefined) { return undefined; } - return "[Channel" + this.activeDeck + "]"; + return `[Channel${this.activeDeck}]`; } if (this.activeDeck === 1 || this.activeDeck === 2) { if (group === "deck1") { return "[Channel1]"; } @@ -1488,7 +1486,7 @@ class HIDController { setCallback(packet, group, name, callback) { const input_packet = this.getInputPacket(packet); if (input_packet === undefined) { - console.error("HIDController.setCallback - Input packet not found " + packet); + console.error(`HIDController.setCallback - Input packet not found ${packet}`); return; } input_packet.setCallback(group, name, callback); @@ -1530,13 +1528,13 @@ class HIDController { linkModifier(group, name, modifier) { const packet = this.getInputPacket(this.defaultPacket); if (packet === undefined) { - console.error("HIDController.linkModifier - Creating modifier: input packet " + this.defaultPacket + " not found"); + console.error(`HIDController.linkModifier - Creating modifier: input packet ${this.defaultPacket} not found`); return; } - const bit_id = group + "." + name; + const bit_id = `${group}.${name}`; const bitField = packet.lookupBit(group, name); if (bitField === undefined) { - console.error("HIDController.linkModifier - Bit field not found: " + bit_id); + console.error(`HIDController.linkModifier - Bit field not found: ${bit_id}`); return; } bitField.group = "modifiers"; @@ -1566,12 +1564,12 @@ class HIDController { linkControl(group, name, m_group, m_name, callback) { const packet = this.getInputPacket(this.defaultPacket); if (packet === undefined) { - console.error("HIDController.linkControl - Creating modifier: input packet " + this.defaultPacket + " not found"); + console.error(`HIDController.linkControl - Creating modifier: input packet ${this.defaultPacket} not found`); return; } const field = packet.getField(group, name); if (field === undefined) { - console.error("HIDController.linkControl - Field not found: " + group + "." + name); + console.error(`HIDController.linkControl - Field not found: ${group}.${name}`); return; } if (field.type !== "bitvector") { @@ -1583,7 +1581,7 @@ class HIDController { } else { const bitField = packet.lookupBit(group, name); if (field === undefined) { - console.error("HIDController.linkControl - Bit not found: " + group + "." + name); + console.error(`HIDController.linkControl - Bit not found: ${group}.${name}`); return; } bitField.mapped_group = m_group; @@ -1715,9 +1713,9 @@ class HIDController { } return; } - console.warn("HIDController.parsePacket - Received unknown packet of " + length + " bytes"); + console.warn(`HIDController.parsePacket - Received unknown packet of ${length} bytes`); for (const i in data) { - console.log("BYTE " + data[i]); + console.log(`BYTE ${data[i]}`); } } /** @@ -1760,7 +1758,7 @@ class HIDController { // Numeric value field this.processControl(field); } else { - console.warn("HIDController.processIncomingPacket - Unknown field " + field.name + " type " + field.type); + console.warn(`HIDController.processIncomingPacket - Unknown field ${field.name} type ${field.type}`); } } } @@ -1778,11 +1776,11 @@ class HIDController { const group = field.group; if (group === undefined) { if (this.activeDeck !== undefined) { - return "[Channel" + this.activeDeck + "]"; + return `[Channel${this.activeDeck}]`; } } if (this.valid_groups.indexOf(group) !== -1) { - // console.log("Resolving group " + group); + // console.log(`Resolving group ${group}`); return this.resolveGroup(group); } return group; @@ -1813,9 +1811,7 @@ class HIDController { if (group === undefined) { console.warn( - "HIDController.processButton - Could not resolve group from " - + field.group + " " + field.mapped_group + " " - + field.name + " " + field.mapped_name); + `HIDController.processButton - Could not resolve group from ${field.group} ${field.mapped_group} ${field.name} ${field.mapped_name}`); return; } @@ -1828,7 +1824,7 @@ class HIDController { return; } if (field.auto_repeat) { - const timer_id = "auto_repeat_" + field.id; + const timer_id = `auto_repeat_${field.id}`; if (field.value) { // @ts-ignore startAutoRepeatTimer needs to be implemented in the users mapping this.startAutoRepeatTimer(timer_id, field.auto_repeat_interval); @@ -1866,7 +1862,7 @@ class HIDController { return; } if (field.auto_repeat && field.value === this.buttonStates.pressed) { - console.log("Callback for " + field.group); + console.log(`Callback for ${field.group}`); engine.setValue(group, control, field.auto_repeat(field)); } else if (engine.getValue(group, control) === 0) { engine.setValue(group, control, true); @@ -1886,13 +1882,11 @@ class HIDController { if (group === undefined) { console.error( - "HIDController.processControl - Could not resolve group from " - + field.group + " " + field.mapped_group + " " - + field.name + " " + field.mapped_name); + `HIDController.processControl - Could not resolve group from ${field.group} ${field.mapped_group} ${field.name} ${field.mapped_name}`); return; } if (field.type === "bitvector") { - console.error("HIDController.processControl - Control refers a field of type bitvector: " + group + "." + control); + console.error(`HIDController.processControl - Control refers a field of type bitvector: ${group}.${control}`); return; } @@ -2068,7 +2062,7 @@ class HIDController { engine.stopTimer(this.timers[timer_id]); delete this.timers[timer_id]; } else { - // console.warn("HIDController.stopAutoRepeatTimer - No such autorepeat timer: " + timer_id); + // console.warn(`HIDController.stopAutoRepeatTimer - No such autorepeat timer: ${timer_id}`); } } /** @@ -2083,7 +2077,7 @@ class HIDController { const packet = this.getInputPacket(this.defaultPacket); const field = packet.getField(group, name); if (field === undefined) { - console.error("HIDController.setAutoRepeat - Field not found " + group + "." + name); + console.error(`HIDController.setAutoRepeat - Field not found ${group}.${name}`); return; } field.auto_repeat = callback; @@ -2143,7 +2137,7 @@ class HIDController { } } const new_group = this.resolveDeckGroup(deck); - console.log("Switching to deck " + deck + " group " + new_group); + console.log(`Switching to deck ${deck} group ${new_group}`); if (this.disconnectDeck !== undefined) { this.disconnectDeck(); } @@ -2164,7 +2158,7 @@ class HIDController { bitControlGroup, bit.mapped_name, bit.mapped_callback, true); engine.connectControl(new_group, bit.mapped_name, bit.mapped_callback); const value = engine.getValue(new_group, bit.mapped_name); - console.log("Bit " + bit.group + "." + bit.name + " value " + value); + console.log(`Bit ${bit.group}.${bit.name} value ${value}`); if (value) { this.setOutput( bit.group, bit.name, @@ -2218,11 +2212,11 @@ class HIDController { linkOutput(group, name, m_group, m_name, callback) { const field = this.getOutputField(group, name); if (field === undefined) { - console.error("HIDController.linkOutput - Linked output not found: " + group + "." + name); + console.error(`HIDController.linkOutput - Linked output not found: ${group}.${name}`); return; } if (field.mapped_group !== undefined) { - console.warn("HIDController.linkOutput - Output already linked: " + field.mapped_group); + console.warn(`HIDController.linkOutput - Output already linked: ${field.mapped_group}`); return; } const controlgroup = this.resolveGroup(m_group); @@ -2246,11 +2240,11 @@ class HIDController { unlinkOutput(group, name, callback) { const field = this.getOutputField(group, name); if (field === undefined) { - console.warn("HIDController.unlinkOutput - Output to be unlinked not found: " + group + "." + name); + console.warn(`HIDController.unlinkOutput - Output to be unlinked not found: ${group}.${name}`); return; } if (field.mapped_group === undefined || field.mapped_name === undefined) { - console.warn("HIDController.unlinkOutput - Output to be unlinked not mapped: " + group + "." + name); + console.warn(`HIDController.unlinkOutput - Output to be unlinked not mapped: ${group}.${name}`); return; } const controlgroup = this.resolveGroup(field.mapped_group); @@ -2271,7 +2265,7 @@ class HIDController { setOutput(group, name, value, send_packet) { const field = this.getOutputField(group, name); if (field === undefined) { - console.error("HIDController.setOutput - Unknown field: " + group + "." + name); + console.error(`HIDController.setOutput - Unknown field: ${group}.${name}`); return; } field.value = Number(value) << field.bit_offset; @@ -2290,7 +2284,7 @@ class HIDController { setOutputToggle(group, name, toggle_value) { const field = this.getOutputField(group, name); if (field === undefined) { - console.error("HIDController.setOutputToggle - Unknown field " + group + "." + name); + console.error(`HIDController.setOutputToggle - Unknown field ${group}.${name}`); return; } field.value = toggle_value << field.bit_offset; From 057ad16911d80e8bacde745c125c628050f9da07 Mon Sep 17 00:00:00 2001 From: Joerg Date: Tue, 11 Oct 2022 20:56:38 +0200 Subject: [PATCH 55/60] Corrected type of packetField.pack Added type for HIDPacket.packSizes Added missing information about endianess of pack types --- res/controllers/common-hid-packet-parser.js | 54 +++++++++++---------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index a496bdf725b..caf2813a61a 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -79,9 +79,9 @@ this.HIDDebug = function(message) { * @property {string} mapped_group Mapped group, must be a valid Mixxx control group name e.g. "[Channel1]" * @property {string} mapped_name Name of mapped control, must be a valid Mixxx control name "VuMeter" * @property {controlCallback} mapped_callback - * @property {Object} pack Control packing format for unpack(), one of b/B, h/H, i/I - * @property {number} offset - * @property {number} end_offset + * @property {string} pack Control packing format for unpack(), one of b/B, h/H, i/I + * @property {number} offset Position of the first byte in the packet in bytes (first byte is 0) + * @property {number} end_offset Position of the last byte in the packet in bytes ({@link packetField.offset} + packet size) * @property {number} bitmask * @property {boolean} isEncoder * @property {fieldChangeCallback} callback @@ -365,7 +365,11 @@ class HIDPacket { */ this.length = this.header.length; - // Size of various 'pack' values in bytes + /** + * Size of the 'pack' types in bytes + * + * @type {Object.} + */ this.packSizes = {b: 1, B: 1, h: 2, H: 2, i: 4, I: 4}; this.signedPackFormats = ["b", "h", "i"]; } @@ -490,13 +494,13 @@ class HIDPacket { * position 0 * - For HID devices which use ReportIDs to enumerate the reports, the * data bytes starts at position 1 - * @param {Object} pack Is one of the field packing types: - * - b signed byte - * - B unsigned byte - * - h signed short - * - H unsigned short - * - i signed integer - * - I unsigned integer + * @param {string} pack Is one of the field packing types: + * - b signed byte (Int8) + * - B unsigned byte (Uint8) + * - h signed short (Int16 Little-Endian) + * - H unsigned short (Uint16 Little-Endian) + * - i signed integer (Int32 Little-Endian) + * - I unsigned integer (Uint32 Little-Endian) * @returns {packetField} Returns matching field or undefined if no matching field can be found. */ getFieldByOffset(offset, pack) { @@ -625,13 +629,13 @@ class HIDPacket { * position 0 * - For HID devices which use ReportIDs to enumerate the reports, the * data bytes starts at position 1 - * @param {Object} pack Is one of the field packing types: - * - b signed byte - * - B unsigned byte - * - h signed short - * - H unsigned short - * - i signed integer - * - I unsigned integer + * @param {string} pack Is one of the field packing types: + * - b signed byte (Int8) + * - B unsigned byte (Uint8) + * - h signed short (Int16 Little-Endian) + * - 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 * considered. * Note: For controls that use full bytes (8bit, 16bit, ...), you can set this to @@ -765,13 +769,13 @@ class HIDPacket { * position 0 * - For HID devices which use ReportIDs to enumerate the reports, the * data bytes starts at position 1 - * @param {Object} pack Is one of the field packing types: - * - b signed byte - * - B unsigned byte - * - h signed short - * - H unsigned short - * - i signed integer - * - I unsigned integer + * @param {string} pack Is one of the field packing types: + * - b signed byte (Int8) + * - B unsigned byte (Uint8) + * - h signed short (Int16 Little-Endian) + * - 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 * considered. * @param {fieldChangeCallback} [callback=undefined] Callback function for the control From 661ddaa6a0a265e3258470a5990ac3aeb10c413a Mon Sep 17 00:00:00 2001 From: Joerg Date: Tue, 11 Oct 2022 21:04:14 +0200 Subject: [PATCH 56/60] Added missing type/type cast --- res/controllers/common-hid-packet-parser.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index caf2813a61a..a9cef259851 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -513,6 +513,7 @@ class HIDPacket { for (const group_name in this.groups) { const group = this.groups[group_name]; for (const field_id in group) { + /** @type {packetField} */ const field = group[field_id]; // Same field offset if (field.offset === offset) { @@ -1858,10 +1859,10 @@ class HIDController { if (control === "play") { engine.setValue(group, "stop", 1); } else { - engine.setValue(group, control, false); + engine.setValue(group, control, Number(false)); } } else { - engine.setValue(group, control, true); + engine.setValue(group, control, Number(true)); } return; } @@ -1869,9 +1870,9 @@ class HIDController { console.log(`Callback for ${field.group}`); engine.setValue(group, control, field.auto_repeat(field)); } else if (engine.getValue(group, control) === 0) { - engine.setValue(group, control, true); + engine.setValue(group, control, Number(true)); } else { - engine.setValue(group, control, false); + engine.setValue(group, control, Number(false)); } } /** @@ -1942,7 +1943,7 @@ class HIDController { return; } const status = Boolean(engine.getValue(group, control)) !== true; - engine.setValue(group, control, status); + engine.setValue(group, control, Number(status)); } /** * Toggle play/pause state @@ -1957,9 +1958,9 @@ class HIDController { } const status = !(engine.getValue(group, "play")); if (!status) { - engine.setValue(group, "stop", true); + engine.setValue(group, "stop", Number(true)); } else { - engine.setValue(group, "play", true); + engine.setValue(group, "play", Number(true)); } } /** From 42923dcf9425022d2ea8bdcc245185d002cee707 Mon Sep 17 00:00:00 2001 From: Joerg Date: Tue, 11 Oct 2022 21:37:33 +0200 Subject: [PATCH 57/60] Added missing @type to HIDPacket.groups --- res/controllers/common-hid-packet-parser.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index a9cef259851..29834acd52f 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -356,6 +356,11 @@ class HIDPacket { */ this.header = header; + /** + * Object of groups, referred by the group string + * + * @type {Object.>} + */ this.groups = {}; /** From 87269eec11449ab366f288768d8da53343e9f811 Mon Sep 17 00:00:00 2001 From: Joerg Date: Tue, 11 Oct 2022 23:16:45 +0200 Subject: [PATCH 58/60] This was the only place where the keyword initializePacketData exist in the Mixxx codebase, and the comment that it don't work is there since the beginning of the Mixxx Git history. --- res/controllers/common-hid-packet-parser.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 29834acd52f..5ebc6cc8b22 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1339,11 +1339,6 @@ class HIDController { this.timers[name] = undefined; } } - /** - * Initialize our packet data and callbacks. This does not seem to - * work when executed from here, but we keep a stub just in case. - */ - initializePacketData() {} /** * Return deck number from deck name. Deck name can't be virtual deck name * in this function call. From ea794af9413f2f869958eaed679a7aae9e0256fd Mon Sep 17 00:00:00 2001 From: Joerg Date: Tue, 11 Oct 2022 23:38:34 +0200 Subject: [PATCH 59/60] Tried to explain, what this.timers = {}; is used for. --- res/controllers/common-hid-packet-parser.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 5ebc6cc8b22..76ac968b9ba 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -1298,7 +1298,9 @@ class HIDController { this.scalers = {}; /** - * Object of engine timer IDs, by hid-parser timer IDs + * 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.} */ @@ -2064,7 +2066,7 @@ class HIDController { */ stopAutoRepeatTimer(timer_id) { if (this.timers[timer_id]) { - engine.stopTimer(this.timers[timer_id]); + engine.stopTimer(this.autorepeat[timer_id]); delete this.timers[timer_id]; } else { // console.warn(`HIDController.stopAutoRepeatTimer - No such autorepeat timer: ${timer_id}`); From f0f184ae0cf6193244532800f79c923a8a564958 Mon Sep 17 00:00:00 2001 From: Joerg Date: Sat, 19 Nov 2022 12:22:22 +0100 Subject: [PATCH 60/60] Fixed accidentally changed return value of HIDModifierList.Get in error case --- res/controllers/common-hid-packet-parser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/common-hid-packet-parser.js b/res/controllers/common-hid-packet-parser.js index 76ac968b9ba..c62addda783 100644 --- a/res/controllers/common-hid-packet-parser.js +++ b/res/controllers/common-hid-packet-parser.js @@ -283,7 +283,7 @@ class HIDModifierList { get(name) { if (!(name in this.modifiers)) { console.error(`HIDModifierList.get - Unknown modifier: ${name}`); - return undefined; + return false; } return this.modifiers[name]; }