summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSwiftb0y <12380386+Swiftb0y@users.noreply.github.com>2023-06-04 22:04:00 +0200
committerSwiftb0y <12380386+Swiftb0y@users.noreply.github.com>2023-06-04 22:04:00 +0200
commit585519d0ef1b76c1d5973c95c80b3ff31f1b5c72 (patch)
tree783691e67fa98cbcb7f94a59a5ca2ed04113252e
parent4a3a8dc39ef3c81d74eec4cd03916d3381ec4c0b (diff)
parentca1ba2f74356fb9b5907333fe3692185cfe78201 (diff)
Merge remote-tracking branch 'upstream/2.4'
-rw-r--r--.eslintrc.json4
-rw-r--r--.github/workflows/build.yml5
-rw-r--r--CMakeLists.txt9
-rw-r--r--lib/benchmark/src/benchmark.cc2
-rw-r--r--lib/fidlib/fidlib.c3
-rw-r--r--lib/libshout-idjc/src/common/net/sock.c6
-rw-r--r--lib/mp3guessenc-0.27.4/decode.c4
-rw-r--r--res/controllers/Traktor Kontrol S4 MK3.hid.xml17
-rw-r--r--res/controllers/Traktor-Kontrol-S4-MK3.js3157
-rw-r--r--res/controllers/common-controller-scripts.js99
-rw-r--r--src/controllers/hid/hidcontroller.h33
-rw-r--r--src/controllers/hid/hidioglobaloutputreportfifo.cpp94
-rw-r--r--src/controllers/hid/hidioglobaloutputreportfifo.h31
-rw-r--r--src/controllers/hid/hidiooutputreport.cpp51
-rw-r--r--src/controllers/hid/hidiooutputreport.h8
-rw-r--r--src/controllers/hid/hidiothread.cpp26
-rw-r--r--src/controllers/hid/hidiothread.h5
-rw-r--r--src/encoder/encoderfdkaac.cpp12
-rw-r--r--src/library/browse/browsefeature.cpp1
-rw-r--r--src/library/treeitemmodel.cpp2
-rw-r--r--src/preferences/dialog/dlgprefinterface.cpp4
-rw-r--r--src/test/controller_mapping_validation_test.cpp8
-rw-r--r--src/test/controller_mapping_validation_test.h4
-rw-r--r--src/util/threadcputimer.h2
-rw-r--r--src/waveform/renderers/allshader/waveformrendermark.cpp3
-rw-r--r--src/waveform/renderers/allshader/waveformrendermark.h2
-rw-r--r--src/widget/openglwindow.cpp23
-rw-r--r--src/widget/openglwindow.h10
-rw-r--r--src/widget/tooltipqopengl.cpp21
-rw-r--r--src/widget/tooltipqopengl.h20
-rw-r--r--src/widget/wglwidgetqglwidget.h2
-rw-r--r--src/widget/wglwidgetqopengl.cpp9
-rw-r--r--src/widget/wglwidgetqopengl.h10
-rw-r--r--src/widget/winitialglwidget.cpp4
-rw-r--r--src/widget/winitialglwidget.h2
-rw-r--r--src/widget/wspinny.cpp4
-rw-r--r--src/widget/wspinnybase.cpp40
-rw-r--r--src/widget/wspinnybase.h4
-rw-r--r--src/widget/wvumeter.cpp4
-rw-r--r--src/widget/wvumeterglsl.cpp4
-rw-r--r--src/widget/wvumeterglsl.h2
-rw-r--r--src/widget/wvumeterlegacy.cpp4
-rw-r--r--src/widget/wvumeterlegacy.h2
43 files changed, 3589 insertions, 168 deletions
diff --git a/.eslintrc.json b/.eslintrc.json
index 54ae380bd6..5c9dc72c7e 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,7 +1,6 @@
{
"extends": [ "eslint:recommended",
- "plugin:jsdoc/recommended",
- "plugin:@typescript-eslint/recommended" ],
+ "plugin:jsdoc/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
@@ -99,6 +98,7 @@
"overrides": [
{
"files": [ "res/controllers/*.d.ts" ],
+ "extends": [ "plugin:@typescript-eslint/recommended" ],
"rules": {
"no-unused-vars": "off"
}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 78e09e48b8..826ad138c4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -109,6 +109,7 @@ jobs:
# also adjust the for the local Windows build setup in
# ./tools/windows_buildenv.bat
cmake_args: >-
+ -DWARNINGS_FATAL=ON
-DBULK=OFF
-DFFMPEG=OFF
-DHSS1394=ON
@@ -354,7 +355,9 @@ jobs:
if: matrix.name == 'Ubuntu 22.04 (gcc)'
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" ]] && [[ "${{ github.repository }}" == "mixxxdj/mixxx" ]]; then
- CPACK_ARGS="-D DEB_UPLOAD_PPA=ppa:mixxx/nightlies -D CPACK_DEBIAN_DEBIAN_VERSION=0ubuntu2"
+ CPACK_ARGS="-D DEB_UPLOAD_PPA=ppa:mixxx/nightlies"
+ elif [[ "${{ github.ref }}" == "refs/heads/2.4" ]] && [[ "${{ github.repository }}" == "mixxxdj/mixxx" ]]; then
+ CPACK_ARGS="-D DEB_UPLOAD_PPA=ppa:mixxx/mixxxbetas"
elif [[ "${{ github.ref }}" =~ ^refs/tags/[0-9]+\.[0-9]+\.[0-9]+$ ]] && [[ "${{ github.repository }}" == "mixxxdj/mixxx" ]]; then
CPACK_ARGS="-D DEB_UPLOAD_PPA=ppa:mixxx/mixxx"
else
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c286725e52..cba68b7a22 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1301,6 +1301,7 @@ if(MSVC)
if(WARNINGS_PEDANTIC)
target_compile_options(mixxx-lib PUBLIC /W4)
else()
+ # Warning Level 3 (production quality)
target_compile_options(mixxx-lib PUBLIC /W3)
target_compile_definitions(mixxx-lib PUBLIC _SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING _CRT_SECURE_NO_WARNINGS)
endif()
@@ -2243,7 +2244,7 @@ add_library(MP3GuessEnc STATIC EXCLUDE_FROM_ALL
lib/mp3guessenc-0.27.4/bit_utils.c
)
if(WIN32)
- target_compile_definitions(MP3GuessEnc PRIVATE __WINDOWS__)
+ target_compile_definitions(MP3GuessEnc PRIVATE __WINDOWS__ _CRT_SECURE_NO_WARNINGS)
endif()
target_include_directories(MP3GuessEnc SYSTEM PUBLIC lib/mp3guessenc-0.27.4)
target_link_libraries(mixxx-lib PRIVATE MP3GuessEnc)
@@ -2885,6 +2886,9 @@ if(BROADCAST)
message(STATUS "Using internal libshout-idjc")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/lib/libshout-idjc")
target_include_directories(mixxx-lib SYSTEM PUBLIC lib/libshout-idjc/include)
+ if(WIN32)
+ target_compile_definitions(shout_mixxx PRIVATE __WINDOWS__ _CRT_NONSTDC_NO_WARNINGS)
+ endif()
target_link_libraries(mixxx-lib PRIVATE shout_mixxx)
else()
target_link_libraries(mixxx-lib PRIVATE Shoutidjc::Shoutidjc)
@@ -3044,6 +3048,7 @@ if(HID)
target_sources(mixxx-lib PRIVATE
src/controllers/hid/hidcontroller.cpp
src/controllers/hid/hidiothread.cpp
+ src/controllers/hid/hidioglobaloutputreportfifo.cpp
src/controllers/hid/hidiooutputreport.cpp
src/controllers/hid/hiddevice.cpp
src/controllers/hid/hidenumerator.cpp
@@ -3164,7 +3169,7 @@ if (NOT CPACK_DEBIAN_PACKAGE_RELEASE)
set(CPACK_DEBIAN_PACKAGE_RELEASE 1)
endif()
-set(CPACK_DEBIAN_DISTRIBUTION_RELEASES jammy kinetic lunar)
+set(CPACK_DEBIAN_DISTRIBUTION_RELEASES focal jammy kinetic lunar mantic)
set(CPACK_DEBIAN_SOURCE_DIR ${CMAKE_SOURCE_DIR})
set(CPACK_DEBIAN_UPLOAD_PPA_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/packaging/CPackDebUploadPPA.cmake")
set(CPACK_DEBIAN_INSTALL_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/packaging/CPackDebInstall.cmake")
diff --git a/lib/benchmark/src/benchmark.cc b/lib/benchmark/src/benchmark.cc
index a086453a94..5cb28b73fe 100644
--- a/lib/benchmark/src/benchmark.cc
+++ b/lib/benchmark/src/benchmark.cc
@@ -378,7 +378,7 @@ void RunBenchmarks(const std::vector<BenchmarkInstance>& benchmarks,
// Disable deprecated warnings temporarily because we need to reference
// CSVReporter but don't want to trigger -Werror=-Wdeprecated-declarations
-#ifdef __GNUC__
+#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
diff --git a/lib/fidlib/fidlib.c b/lib/fidlib/fidlib.c
index e93de8ca3d..2867222ff6 100644
--- a/lib/fidlib/fidlib.c
+++ b/lib/fidlib/fidlib.c
@@ -1681,12 +1681,11 @@ auto_adjust_dual(Spec *sp, double rate, double f0, double f1) {
double r0, r1, err0, err1;
double perr;
int cnt;
- int cnt_design= 0;
#define DESIGN(mm,ww) { if (rv) {free(rv);rv= 0;} \
rv= design(rate, mm-ww, mm+ww, sp->order, sp->n_arg, sp->argarr); \
r0= fid_response(rv, f0); r1= fid_response(rv, f1); \
- err0= fabs(M301DB-r0); err1= fabs(M301DB-r1); cnt_design++; }
+ err0= fabs(M301DB-r0); err1= fabs(M301DB-r1); }
#define INC_WID ((r0+r1 < 1.0) == bpass)
#define INC_MID ((r0 > r1) == bpass)
diff --git a/lib/libshout-idjc/src/common/net/sock.c b/lib/libshout-idjc/src/common/net/sock.c
index 70053a641b..7ea67602ee 100644
--- a/lib/libshout-idjc/src/common/net/sock.c
+++ b/lib/libshout-idjc/src/common/net/sock.c
@@ -194,10 +194,12 @@ int sock_stalled (int error)
}
+#ifdef HAVE_GETADDRINFO
static int sock_connect_pending (int error)
{
return error == EINPROGRESS || error == EALREADY;
}
+#endif
/* sock_valid_socket
**
@@ -262,7 +264,7 @@ int sock_set_blocking(sock_t sock, int block)
#ifdef __MINGW32__
u_long varblock = 1;
#else
- int varblock = 1;
+ u_long varblock = 1;
#endif
#endif
@@ -786,7 +788,7 @@ int sock_try_connection (sock_t sock, const char *hostname, unsigned int port)
return -1;
}
- memcpy(&server.sin_addr, &sin.sin_addr, sizeof(struct sockaddr_in));
+ memcpy(&server.sin_addr, &sin.sin_addr, sizeof(IN_ADDR));
server.sin_family = AF_INET;
server.sin_port = htons((short)port);
diff --git a/lib/mp3guessenc-0.27.4/decode.c b/lib/mp3guessenc-0.27.4/decode.c
index ad60ac4ed1..3fff559443 100644
--- a/lib/mp3guessenc-0.27.4/decode.c
+++ b/lib/mp3guessenc-0.27.4/decode.c
@@ -20,6 +20,10 @@
#include <stdlib.h>
#include <string.h>
+#ifdef _MSC_VER
+#define strcasecmp _stricmp
+#endif
+
/*
* This takes the command line strings starting at optind and concatenate them in order
* to build a long string. Then this string will be compared with the actual key (codename).
diff --git a/res/controllers/Traktor Kontrol S4 MK3.hid.xml b/res/controllers/Traktor Kontrol S4 MK3.hid.xml
new file mode 100644
index 0000000000..02b9b5b936
--- /dev/null
+++ b/res/controllers/Traktor Kontrol S4 MK3.hid.xml
@@ -0,0 +1,17 @@
+<?xml version='1.0' encoding='utf-8'?>
+<MixxxControllerPreset mixxxVersion="2.4.0" schemaVersion="1">
+ <info>
+ <name>Traktor Kontrol S4 MK3</name>
+ <author>Be, A. Colombier</author>
+ <description>HID Mapping for Traktor Kontrol S4 MK3</description>
+ <manual>native_instruments_traktor_kontrol_s4_mk3</manual>
+ <devices>
+ <product protocol="hid" vendor_id="0x17cc" product_id="0x1720" usage_page="0xff01" usage="0x1" interface_number="0x4" />
+ </devices>
+ </info>
+ <controller id="Traktor">
+ <scriptfiles>
+ <file filename="Traktor-Kontrol-S4-MK3.js" functionprefix="TraktorS4MK3"/>
+ </scriptfiles>
+ </controller>
+</MixxxControllerPreset>
diff --git a/res/controllers/Traktor-Kontrol-S4-MK3.js b/res/controllers/Traktor-Kontrol-S4-MK3.js
new file mode 100644
index 0000000000..b20056345a
--- /dev/null
+++ b/res/controllers/Traktor-Kontrol-S4-MK3.js
@@ -0,0 +1,3157 @@
+/// Created by Be <be@mixxx.org> and A. Colombier <mixxx@acolombier.dev>
+
+const LedColors = {
+ off: 0,
+ red: 4,
+ carrot: 8,
+ orange: 12,
+ honey: 16,
+ yellow: 20,
+ lime: 24,
+ green: 28,
+ aqua: 32,
+ celeste: 36,
+ sky: 40,
+ blue: 44,
+ purple: 48,
+ fuscia: 52,
+ magenta: 56,
+ azalea: 60,
+ salmon: 64,
+ white: 68,
+};
+
+
+// This define the sequence of color to use for pad button when in keyboard mode. This should make them look like an actual keyboard keyboard octave, except for C, which is green to help spotting it.
+const KeyboardColors = [
+ LedColors.green,
+ LedColors.off,
+ LedColors.white,
+ LedColors.off,
+ LedColors.white,
+ LedColors.white,
+ LedColors.off,
+ LedColors.white,
+ LedColors.off,
+ LedColors.white,
+ LedColors.off,
+ LedColors.white,
+];
+
+/*
+ * USER CONFIGURABLE SETTINGS
+ * Adjust these to your liking
+ */
+
+const DeckColors = [
+ LedColors.red,
+ LedColors.blue,
+ LedColors.yellow,
+ LedColors.purple,
+];
+
+const LibrarySortableColumns = [
+ script.LIBRARY_COLUMNS.ARTIST,
+ script.LIBRARY_COLUMNS.TITLE,
+ script.LIBRARY_COLUMNS.BPM,
+ script.LIBRARY_COLUMNS.KEY,
+ script.LIBRARY_COLUMNS.DATETIME_ADDED,
+];
+
+const LoopWheelMoveFactor = 50;
+const LoopEncoderMoveFactor = 500;
+const LoopEncoderShiftmoveFactor = 2500;
+
+const TempoFaderSoftTakeoverColorLow = LedColors.white;
+const TempoFaderSoftTakeoverColorHigh = LedColors.green;
+
+// Define whether or not to keep LED that have only one color (reverse, flux, play, shift) dimmed if they are inactive.
+// 'true' will keep them dimmed, 'false' will turn them off. Default: true
+const KeepLEDWithOneColorDimedWhenInactive = true;
+
+// Keep both deck select buttons backlit and do not fully turn off the inactive deck button.
+// 'true' will keep the unseclected deck dimmed, 'false' to fully turn it off. Default: true
+const KeepDeckSelectDimmed = true;
+
+// Define whether the keylock is mapped when doing "shift+master" (on press) or "shift+sync" (on release since long push copies the key)".
+// 'true' will use "sync+master", 'false' will use "shift+sync". Default: false
+const UseKeylockOnMaster = false;
+
+// Define whether the grid button would blink when the playback is going over a detcted beat. Can help to adjust beat grid.
+// Default: false
+const GridButtonBlinkOverBeat = false;
+
+// Wheel led blinking if reaching the end of track warning (default 30 seconds, can be changed in the settings, under "Waveforms" > "End of track warning").
+// Default: true
+const WheelLedBlinkOnTrackEnd = true;
+
+// When shifting either decks, the mixer will control microphones or auxiliary lines. If there is both a mic and an configure on the same channel, the mixer will control the auxiliary.
+// Default: false
+const MixerControlsMixAuxOnShift = false;
+
+// Define how many wheel moves are sampled to compute the speed. The more you have, the more the speed is accurate, but the
+// less responsive it gets in Mixxx. Default: 5
+const WheelSpeedSample = 3;
+
+// Make the sampler tab a beatlooproll tab instead
+// Default: false
+const UseBeatloopRollInsteadOfSampler = false;
+
+// Predefined beatlooproll sizes. Note that if you use AddLoopHalveAndDoubleOnBeatloopRollTab, the first and
+// last size will be ignored
+const BeatLoopRolls = [1/16, 1/8, 1/4, 1/2, 1, 2, 4, 8];
+
+// Make the two last button on the beatlooproll pad halve or double the loop size. This will take away the 1/16 and 8 loop size.
+// Default: true
+const AddLoopHalveAndDoubleOnBeatloopRollTab = true;
+
+// Define the speed of the jogwheel. This will impact the speed of the LED playback indicator, the sratch, and the speed of
+// the motor if enable. Recommended value are 33 + 1/3 or 45.
+// Default: 33 + 1/3
+const BaseRevolutionsPerMinute = 33 + 1/3;
+
+// Define whether or not to use motors.
+// This is a BETA feature! Please use at your own risk. Setting this off means that below settings are inactive
+// Default: false
+const UseMotors = false;
+
+// Define how many wheel moves are sampled to compute the speed when using the motor. This is helpful to mitigate delay that
+// occurs in communication as well as Mixxx limitation to 20ms latency.
+// The more you have, the more the speed is accurate.
+// less responsive it gets in Mixxx. Default: 20
+const TurnTableSpeedSample = 20;
+
+// Define how much the wheel will resist. It is a similar setting that the Grid+Wheel in Tracktor
+// Value must defined between 0 to 1. 0 is very tight, 1 is very loose.
+// Default: 0.5
+const TightnessFactor = 0.5;
+
+// Define how much force can the motor use. This defines how much the wheel will "fight" you when you block it in TT mode
+// This will also affect how quick the wheel starts spinning when enabling motor mode, or starting a deck with motor mode on
+const MaxWheelForce = 25000; // Traktor seems to cap the max value at 60000, which just sounds insane
+
+
+
+// The LEDs only support 16 base colors. Adding 1 in addition to
+// the normal 2 for Button.prototype.brightnessOn changes the color
+// slightly, so use that get 25 different colors to include the Filter
+// button as a 5th effect chain preset selector.
+const QuickEffectPresetColors = [
+ LedColors.red,
+ LedColors.green,
+ LedColors.blue,
+ LedColors.yellow,
+ LedColors.orange,
+ LedColors.purple,
+ LedColors.white,
+
+ LedColors.magenta,
+ LedColors.azalea,
+ LedColors.salmon,
+ LedColors.red + 1,
+
+ LedColors.sky,
+ LedColors.celeste,
+ LedColors.fuscia,
+ LedColors.blue + 1,
+
+ LedColors.carrot,
+ LedColors.honey,
+ LedColors.yellow + 1,
+
+ LedColors.lime,
+ LedColors.aqua,
+ LedColors.purple + 1,
+
+ LedColors.magenta + 1,
+ LedColors.azalea + 1,
+ LedColors.salmon + 1,
+ LedColors.fuscia + 1,
+];
+
+// assign samplers to the crossfader on startup
+const SamplerCrossfaderAssign = true;
+
+const MotorWindUpMilliseconds = 1200;
+const MotorWindDownMilliseconds = 900;
+
+/*
+ * HID report parsing library
+ */
+class HIDInputReport {
+ constructor(reportId) {
+ this.reportId = reportId;
+ this.fields = [];
+ }
+
+ registerCallback(callback, byteOffset, bitOffset = 0, bitLength = 1, defaultOldData = undefined) {
+ if (typeof callback !== "function") {
+ throw Error("callback must be a function");
+ }
+
+ if (!Number.isInteger(byteOffset)) {
+ throw Error("byteOffset must be 0 or a positive integer");
+ }
+ if (!Number.isInteger(bitOffset) || bitOffset < 0) {
+ throw Error("bitOffset must be 0 or a positive integer");
+ }
+ if (!Number.isInteger(bitOffset) || bitLength < 1 || bitLength > 32) {
+ throw Error("bitLength must be an integer between 1 and 32");
+ }
+
+ const field = {
+ callback: callback,
+ byteOffset: byteOffset,
+ bitOffset: bitOffset,
+ bitLength: bitLength,
+ oldData: defaultOldData
+ };
+ this.fields.push(field);
+
+ return {
+ disconnect: () => {
+ this.fields = this.fields.filter((element) => {
+ return element !== field;
+ });
+ }
+ };
+ }
+
+ handleInput(reportData) {
+ const view = new DataView(reportData);
+
+ for (const field of this.fields) {
+ const numBytes = Math.ceil(field.bitLength / 8);
+ let data;
+
+ // Little endianness is specified by the HID standard.
+ // The HID standard allows signed integers as well, but I am not aware
+ // of any HID DJ controllers which use signed integers.
+ if (numBytes === 1) {
+ data = view.getUint8(field.byteOffset);
+ } else if (numBytes === 2) {
+ data = view.getUint16(field.byteOffset, true);
+ } else if (numBytes === 3) {
+ data = view.getUint32(field.byteOffset, true) >>> 8;
+ } else if (numBytes === 4) {
+ data = view.getUint32(field.byteOffset, true);
+ } else {
+ throw Error("field bitLength must be between 1 and 32");
+ }
+
+ // The >>> 0 is required for 32 bit unsigned ints to not magically turn negative
+ // because all Numbers are really 32 bit signed floats. Because JavaScript.
+ data = ((data >> field.bitOffset) & (2 ** field.bitLength - 1)) >>> 0;
+
+ if (field.oldData !== data) {
+ field.callback(data);
+ field.oldData = data;
+ }
+ }
+ }
+}
+
+class HIDOutputReport {
+ constructor(reportId, length) {
+ this.reportId = reportId;
+ this.data = new Uint8Array(length).fill(0);
+ }
+ send() {
+ controller.sendOutputReport(this.reportId, this.data.buffer);
+ }
+}
+
+/*
+ * Components library
+ */
+
+class Component {
+ constructor(options) {
+ if (options) {
+ Object.keys(options).forEach(function(key) {
+ if (options[key] === undefined) { delete options[key]; }
+ });
+ Object.assign(this, options);
+ }
+ this.outConnections = [];
+ if (typeof this.key === "string") {
+ this.inKey = this.key;
+ this.outKey = this.key;
+ }
+ if (typeof this.unshift === "function" && this.unshift.length === 0) {
+ this.unshift();
+ }
+ this.shifted = false;
+ if (typeof this.input === "function" && this.inReport instanceof HIDInputReport && this.inReport.length === 0) {
+ this.inConnect();
+ }
+ this.outConnect();
+ }
+ inConnect(callback) {
+ if (this.inByte === undefined
+ || this.inBit === undefined
+ || this.inBitLength === undefined
+ || this.inReport === undefined) {
+ return;
+ }
+ if (typeof callback === "function") {
+ this.input = callback;
+ }
+ this.inConnection = this.inReport.registerCallback(this.input.bind(this), this.inByte, this.inBit, this.inBitLength, this.oldDataDefault);
+ }
+ inDisconnect() {
+ if (this.inConnection !== undefined) {
+ this.inConnection.disconnect();
+ }
+ }
+ send(value) {
+ if (this.outReport !== undefined && this.outByte !== undefined) {
+ this.outReport.data[this.outByte] = value;
+ this.outReport.send();
+ }
+ }
+ output(value) {
+ this.send(value);
+ }
+ outConnect() {
+ if (this.outKey !== undefined && this.group !== undefined) {
+ const connection = engine.makeConnection(this.group, this.outKey, this.output.bind(this));
+ // This is useful for case where effect would have been fully disabled in Mixxx. This appears to be the case during unit tests.
+ if (connection) {
+ this.outConnections[0] = connection;
+ } else {
+ console.warn(`Unable to connect ${this.group}.${this.outKey}' to the controller output. The control appears to be unavailable.`);
+ }
+ }
+ }
+ outDisconnect() {
+ for (const connection of this.outConnections) {
+ connection.disconnect();
+ }
+ this.outConnections = [];
+ }
+ outTrigger() {
+ for (const connection of this.outConnections) {
+ connection.trigger();
+ }
+ }
+}
+class ComponentContainer extends Component {
+ constructor() {
+ super();
+ }
+ *[Symbol.iterator]() {
+ // can't use for...of here because it would create an infinite loop
+ for (const property in this) {
+ if (Object.prototype.hasOwnProperty.call(this, property)) {
+ const obj = this[property];
+ if (obj instanceof Component) {
+ yield obj;
+ } else if (Array.isArray(obj)) {
+ for (const objectInArray of obj) {
+ if (objectInArray instanceof Component) {
+ yield objectInArray;
+ }
+ }
+ }
+ }
+ }
+ }
+ reconnectComponents(callback) {
+ for (const component of this) {
+ if (typeof component.outDisconnect === "function" && component.outDisconnect.length === 0) {
+ component.outDisconnect();
+ }
+ if (typeof callback === "function" && callback.length === 1) {
+ callback.call(this, component);
+ }
+ if (typeof component.outConnect === "function" && component.outConnect.length === 0) {
+ component.outConnect();
+ }
+ component.outTrigger();
+ if (typeof component.unshift === "function" && component.unshift.length === 0) {
+ component.unshift();
+ }
+ }
+ }
+ unshift() {
+ for (const component of this) {
+ if (typeof component.unshift === "function" && component.unshift.length === 0) {
+ component.unshift();
+ }
+ component.shifted = false;
+ }
+ this.shifted = false;
+ }
+ shift() {
+ for (const component of this) {
+ if (typeof component.shift === "function" && component.shift.length === 0) {
+ component.shift();
+ }
+ component.shifted = true;
+ }
+ this.shifted = true;
+ }
+}
+
+/* eslint no-redeclare: "off" */
+class Deck extends ComponentContainer {
+ constructor(decks, colors) {
+ super();
+ if (typeof decks === "number") {
+ this.group = Deck.groupForNumber(decks);
+ } else if (Array.isArray(decks)) {
+ this.decks = decks;
+ this.currentDeckNumber = decks[0];
+ this.group = Deck.groupForNumber(decks[0]);
+ }
+ if (colors !== undefined && Array.isArray(colors)) {
+ this.groupsToColors = {};
+ let index = 0;
+ for (const deck of this.decks) {
+ this.groupsToColors[Deck.groupForNumber(deck)] = colors[index];
+ index++;
+ }
+ this.color = colors[0];
+ }
+ this.secondDeckModes = null;
+ }
+ toggleDeck() {
+ if (this.decks === undefined) {
+ throw Error("toggleDeck can only be used with Decks constructed with an Array of deck numbers, for example [1, 3]");
+ }
+
+ const currentDeckIndex = this.decks.indexOf(this.currentDeckNumber);
+ let newDeckIndex = currentDeckIndex + 1;
+ if (currentDeckIndex >= this.decks.length) {
+ newDeckIndex = 0;
+ }
+
+ this.switchDeck(Deck.groupForNumber(this.decks[newDeckIndex]));
+ }
+ switchDeck(newGroup) {
+ const currentModes = {
+ wheelMode: this.wheelMode,
+