From 877757ddb00dc78e261d24d9b57a98218797f373 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 10 Nov 2021 19:09:04 +0100 Subject: [PATCH 01/18] Controllers: Add Mapping for Reloop Ready Co-authored-by: majekw --- res/controllers/Reloop Ready.midi.xml | 2143 +++++++++++++++++++++++ res/controllers/Reloop-Ready-scripts.js | 1141 ++++++++++++ 2 files changed, 3284 insertions(+) create mode 100644 res/controllers/Reloop Ready.midi.xml create mode 100644 res/controllers/Reloop-Ready-scripts.js diff --git a/res/controllers/Reloop Ready.midi.xml b/res/controllers/Reloop Ready.midi.xml new file mode 100644 index 00000000000..c58c7e55c77 --- /dev/null +++ b/res/controllers/Reloop Ready.midi.xml @@ -0,0 +1,2143 @@ + + + + Reloop Ready + Swiftb0y + Almost feature-complete mapping of the Reloop Ready hardware. Development kindly sponsored by Samgarr + + + + + + + + + + + + + + + ReloopReady.components.leftDeck.jog.inputTouch + 0x90 + 0x06 + + + + + + ReloopReady.components.leftDeck.jog.inputTouch + 0x80 + 0x06 + + + + + + + ReloopReady.components.leftDeck.jog.inputWheel + 0xB0 + 0x06 + + + + + + + + ReloopReady.components.rightDeck.jog.inputTouch + 0x91 + 0x06 + + + + + + ReloopReady.components.rightDeck.jog.inputTouch + 0x81 + 0x06 + + + + + + + ReloopReady.components.rightDeck.jog.inputWheel + 0xB1 + 0x06 + + + + + + + + + ReloopReady.components.leftChannel.knob1.input + 0xB0 + 0x16 + + + + + + + ReloopReady.components.leftChannel.knob2.input + 0xB0 + 0x17 + + + + + + + ReloopReady.components.leftChannel.knob3.input + 0xB0 + 0x19 + + + + + + + ReloopReady.components.leftChannel.filter.input + 0xB0 + 0x1A + + + + + + + ReloopReady.components.leftChannel.volume.input + 0xB0 + 0x1C + + + + + + + ReloopReady.components.leftChannel.rate.inputMSB + 0xB0 + 0x09 + + + + + + + ReloopReady.components.leftChannel.rate.inputLSB + 0xB0 + 0x3F + + + + + + + + ReloopReady.components.leftChannel.load.input + 0x9E + 0x02 + + + + + + + ReloopReady.components.leftChannel.load.input + 0x8E + 0x02 + + + + + + + + ReloopReady.components.leftChannel.pfl.input + 0x90 + 0x1B + + + + + + + ReloopReady.components.leftChannel.pfl.input + 0x80 + 0x1B + + + + + + + + + ReloopReady.components.rightChannel.knob1.input + 0xB1 + 0x16 + + + + + + + ReloopReady.components.rightChannel.knob2.input + 0xB1 + 0x17 + + + + + + + ReloopReady.components.rightChannel.knob3.input + 0xB1 + 0x19 + + + + + + + ReloopReady.components.rightChannel.filter.input + 0xB1 + 0x1A + + + + + + + ReloopReady.components.rightChannel.volume.input + 0xB1 + 0x1C + + + + + + + ReloopReady.components.rightChannel.rate.inputMSB + 0xB1 + 0x09 + + + + + + + ReloopReady.components.rightChannel.rate.inputLSB + 0xB1 + 0x3F + + + + + + + + ReloopReady.components.rightChannel.load.input + 0x9E + 0x03 + + + + + + + ReloopReady.components.rightChannel.load.input + 0x8E + 0x03 + + + + + + + + ReloopReady.components.rightChannel.pfl.input + 0x91 + 0x1B + + + + + + + ReloopReady.components.rightChannel.pfl.input + 0x81 + 0x1B + + + + + + + + ReloopReady.components.leftDeck.fxUnit.level.input + 0xB8 + 0x00 + + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[0].input + 0x98 + 0x00 + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[0].input + 0x88 + 0x00 + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[1].input + 0x98 + 0x01 + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[1].input + 0x88 + 0x01 + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[2].input + 0x98 + 0x02 + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[2].input + 0x88 + 0x02 + + + + + + + ReloopReady.components.leftDeck.fxUnit.loop.knob.input + 0xB4 + 0x34 + + + + + + + ReloopReady.components.leftDeck.fxUnit.loop.button.input + 0x94 + 0x40 + + + + + + + ReloopReady.components.leftDeck.fxUnit.loop.button.input + 0x84 + 0x40 + + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[0].input + 0x98 + 0x0A + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[0].input + 0x88 + 0x0A + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[1].input + 0x98 + 0x0B + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[1].input + 0x88 + 0x0B + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[2].input + 0x98 + 0x0C + + + + + + + ReloopReady.components.leftDeck.fxUnit.fxButtons[2].input + 0x88 + 0x0C + + + + + + + ReloopReady.components.leftDeck.fxUnit.loop.knob.input + 0xB4 + 0x03 + + + + + + + ReloopReady.components.leftDeck.fxUnit.loop.button.input + 0x94 + 0x04 + + + + + + + ReloopReady.components.leftDeck.fxUnit.loop.button.input + 0x84 + 0x04 + + + + + + + + + ReloopReady.components.rightDeck.fxUnit.level.input + 0xB9 + 0x00 + + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[0].input + 0x99 + 0x00 + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[0].input + 0x89 + 0x00 + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[1].input + 0x99 + 0x01 + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[1].input + 0x89 + 0x01 + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[2].input + 0x99 + 0x02 + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[2].input + 0x89 + 0x02 + + + + + + + ReloopReady.components.rightDeck.fxUnit.loop.knob.input + 0xB5 + 0x34 + + + + + + + ReloopReady.components.rightDeck.fxUnit.loop.button.input + 0x95 + 0x40 + + + + + + + ReloopReady.components.rightDeck.fxUnit.loop.button.input + 0x85 + 0x40 + + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[0].input + 0x99 + 0x0A + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[0].input + 0x89 + 0x0A + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[1].input + 0x99 + 0x0B + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[1].input + 0x89 + 0x0B + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[2].input + 0x99 + 0x0C + + + + + + + ReloopReady.components.rightDeck.fxUnit.fxButtons[2].input + 0x89 + 0x0C + + + + + + + ReloopReady.components.rightDeck.fxUnit.loop.knob.input + 0xB5 + 0x03 + + + + + + + ReloopReady.components.rightDeck.fxUnit.loop.button.input + 0x95 + 0x04 + + + + + + + ReloopReady.components.rightDeck.fxUnit.loop.button.input + 0x85 + 0x04 + + + + + + + + ReloopReady.components.leftDeck.play.input + 0x90 + 0x00 + + + + + + + ReloopReady.components.leftDeck.play.input + 0x80 + 0x00 + + + + + + + ReloopReady.components.leftDeck.play.input + 0x90 + 0x10 + + + + + + + ReloopReady.components.leftDeck.play.input + 0x80 + 0x10 + + + + + + + + ReloopReady.components.rightDeck.play.input + 0x91 + 0x00 + + + + + + + ReloopReady.components.rightDeck.play.input + 0x81 + 0x00 + + + + + + + ReloopReady.components.rightDeck.play.input + 0x91 + 0x10 + + + + + + + ReloopReady.components.rightDeck.play.input + 0x81 + 0x10 + + + + + + + + + + + ReloopReady.components.leftDeck.cue.input + 0x90 + 0x01 + + + + + + + ReloopReady.components.leftDeck.cue.input + 0x80 + 0x01 + + + + + + + ReloopReady.components.leftDeck.cue.input + 0x90 + 0x05 + + + + + + + ReloopReady.components.leftDeck.cue.input + 0x80 + 0x05 + + + + + + + + ReloopReady.components.rightDeck.cue.input + 0x91 + 0x01 + + + + + + + ReloopReady.components.rightDeck.cue.input + 0x81 + 0x01 + + + + + + + ReloopReady.components.rightDeck.cue.input + 0x91 + 0x05 + + + + + + + ReloopReady.components.rightDeck.cue.input + 0x81 + 0x05 + + + + + + + + + + + + ReloopReady.components.leftDeck.sync.input + 0x90 + 0x02 + + + + + + + ReloopReady.components.leftDeck.sync.input + 0x80 + 0x02 + + + + + + + ReloopReady.components.leftDeck.sync.input + 0x90 + 0x03 + + + + + + + ReloopReady.components.leftDeck.sync.input + 0x80 + 0x03 + + + + + + + + ReloopReady.components.rightDeck.sync.input + 0x91 + 0x02 + + + + + + + ReloopReady.components.rightDeck.sync.input + 0x81 + 0x02 + + + + + + + ReloopReady.components.rightDeck.sync.input + 0x91 + 0x03 + + + + + + + ReloopReady.components.rightDeck.sync.input + 0x81 + 0x03 + + + + + + + + + ReloopReady.components.leftDeck.vinyl.input + 0x90 + 0x0F + + + + + + + ReloopReady.components.leftDeck.vinyl.input + 0x80 + 0x0F + + + + + + + ReloopReady.components.leftDeck.vinyl.input + 0x90 + 0x07 + + + + + + + ReloopReady.components.leftDeck.vinyl.input + 0x80 + 0x07 + + + + + + + + ReloopReady.components.rightDeck.vinyl.input + 0x91 + 0x0F + + + + + + + ReloopReady.components.rightDeck.vinyl.input + 0x81 + 0x0F + + + + + + + ReloopReady.components.rightDeck.vinyl.input + 0x91 + 0x07 + + + + + + + ReloopReady.components.rightDeck.vinyl.input + 0x81 + 0x07 + + + + + + + + + ReloopReady.components.leftDeck.keylock.input + 0x90 + 0x0D + + + + + + + ReloopReady.components.leftDeck.keylock.input + 0x80 + 0x0D + + + + + + + ReloopReady.components.leftDeck.keylock.input + 0x90 + 0x29 + + + + + + + ReloopReady.components.leftDeck.keylock.input + 0x80 + 0x29 + + + + + + + + ReloopReady.components.rightDeck.keylock.input + 0x91 + 0x0D + + + + + + + ReloopReady.components.rightDeck.keylock.input + 0x81 + 0x0D + + + + + + + ReloopReady.components.rightDeck.keylock.input + 0x91 + 0x29 + + + + + + + ReloopReady.components.rightDeck.keylock.input + 0x81 + 0x29 + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[0].input + 0x94 + 0x14 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[0].input + 0x84 + 0x14 + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[1].input + 0x94 + 0x15 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[1].input + 0x84 + 0x15 + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[2].input + 0x94 + 0x16 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[2].input + 0x84 + 0x16 + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[3].input + 0x94 + 0x17 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[3].input + 0x84 + 0x17 + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[4].input + 0x94 + 0x18 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[4].input + 0x84 + 0x18 + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[5].input + 0x94 + 0x19 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[5].input + 0x84 + 0x19 + + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[6].input + 0x94 + 0x1A + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[6].input + 0x84 + 0x1A + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[7].input + 0x94 + 0x1B + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[7].input + 0x84 + 0x1B + + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[0].input + 0x94 + 0x1C + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[0].input + 0x84 + 0x1C + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[1].input + 0x94 + 0x1D + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[1].input + 0x84 + 0x1D + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[2].input + 0x94 + 0x1E + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[2].input + 0x84 + 0x1E + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[3].input + 0x94 + 0x1F + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[3].input + 0x84 + 0x1F + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[4].input + 0x94 + 0x20 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[4].input + 0x84 + 0x20 + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[5].input + 0x94 + 0x21 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[5].input + 0x84 + 0x21 + + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[6].input + 0x94 + 0x22 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[6].input + 0x84 + 0x22 + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[7].input + 0x94 + 0x23 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.pads[7].input + 0x84 + 0x23 + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[0].input + 0x95 + 0x14 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[0].input + 0x85 + 0x14 + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[1].input + 0x95 + 0x15 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[1].input + 0x85 + 0x15 + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[2].input + 0x95 + 0x16 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[2].input + 0x85 + 0x16 + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[3].input + 0x95 + 0x17 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[3].input + 0x85 + 0x17 + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[4].input + 0x95 + 0x18 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[4].input + 0x85 + 0x18 + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[5].input + 0x95 + 0x19 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[5].input + 0x85 + 0x19 + + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[6].input + 0x95 + 0x1A + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[6].input + 0x85 + 0x1A + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[7].input + 0x95 + 0x1B + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[7].input + 0x85 + 0x1B + + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[0].input + 0x95 + 0x1C + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[0].input + 0x85 + 0x1C + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[1].input + 0x95 + 0x1D + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[1].input + 0x85 + 0x1D + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[2].input + 0x95 + 0x1E + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[2].input + 0x85 + 0x1E + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[3].input + 0x95 + 0x1F + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[3].input + 0x85 + 0x1F + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[4].input + 0x95 + 0x20 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[4].input + 0x85 + 0x20 + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[5].input + 0x95 + 0x21 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[5].input + 0x85 + 0x21 + + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[6].input + 0x95 + 0x22 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[6].input + 0x85 + 0x22 + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[7].input + 0x95 + 0x23 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.pads[7].input + 0x85 + 0x23 + + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.parameterRight.input + 0x95 + 0x29 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.parameterRight.input + 0x85 + 0x29 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.parameterLeft.input + 0x95 + 0x28 + + + + + + + ReloopReady.components.rightDeck.padUnit.currentLayer.parameterLeft.input + 0x85 + 0x28 + + + + + + + + ReloopReady.components.rightDeck.padUnit.modeButton.input + 0x91 + 0x20 + + + + + + + ReloopReady.components.rightDeck.padUnit.modeButton.input + 0x81 + 0x20 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[0].input + 0x95 + 0x00 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[0].input + 0x85 + 0x00 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[1].input + 0x95 + 0x05 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[1].input + 0x85 + 0x05 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[2].input + 0x95 + 0x0E + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[2].input + 0x85 + 0x0E + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[3].input + 0x95 + 0x0B + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[3].input + 0x85 + 0x0B + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[4].input + 0x95 + 0x0F + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[4].input + 0x85 + 0x0F + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[5].input + 0x95 + 0x09 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[5].input + 0x85 + 0x09 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[6].input + 0x95 + 0x08 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[6].input + 0x85 + 0x08 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[7].input + 0x95 + 0x12 + + + + + + + ReloopReady.components.rightDeck.padUnit.padModeSelectors[7].input + 0x85 + 0x12 + + + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.parameterRight.input + 0x94 + 0x29 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.parameterRight.input + 0x84 + 0x29 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.parameterLeft.input + 0x94 + 0x28 + + + + + + + ReloopReady.components.leftDeck.padUnit.currentLayer.parameterLeft.input + 0x84 + 0x28 + + + + + + + ReloopReady.components.leftDeck.padUnit.modeButton.input + 0x90 + 0x20 + + + + + + + ReloopReady.components.leftDeck.padUnit.modeButton.input + 0x80 + 0x20 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[0].input + 0x94 + 0x00 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[0].input + 0x84 + 0x00 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[1].input + 0x94 + 0x05 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[1].input + 0x84 + 0x05 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[2].input + 0x94 + 0x0E + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[2].input + 0x84 + 0x0E + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[3].input + 0x94 + 0x0B + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[3].input + 0x84 + 0x0B + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[4].input + 0x94 + 0x0F + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[4].input + 0x84 + 0x0F + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[5].input + 0x94 + 0x09 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[5].input + 0x84 + 0x09 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[6].input + 0x94 + 0x08 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[6].input + 0x84 + 0x08 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[7].input + 0x94 + 0x12 + + + + + + + ReloopReady.components.leftDeck.padUnit.padModeSelectors[7].input + 0x84 + 0x12 + + + + + + + + ReloopReady.shift.input + 0x9E + 0x00 + + + + + + + ReloopReady.shift.input + 0x8E + 0x00 + + + + + + + + ReloopReady.components.crossfader.input + 0xBE + 0x08 + + + + + + + + ReloopReady.components.cueVol.input + 0xBE + 0x0C + + + + + + + ReloopReady.components.cueMix.input + 0xBE + 0x0D + + + + + + + ReloopReady.components.masterVol.input + 0xBE + 0x0A + + + + + + + + ReloopReady.components.browse.button.input + 0x9E + 0x06 + + + + + + + ReloopReady.components.browse.button.input + 0x8E + 0x06 + + + + + + + ReloopReady.components.browse.button.input + 0x9E + 0x7B + + + + + + + ReloopReady.components.browse.button.input + 0x8E + 0x7B + + + + + + + + ReloopReady.components.browse.knob.input + 0xBE + 0x00 + + + + + + + ReloopReady.components.browse.knob.input + 0xBE + 0x78 + + + + + + + + 0xF0 + + + + + + + diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js new file mode 100644 index 00000000000..c0c06035101 --- /dev/null +++ b/res/controllers/Reloop-Ready-scripts.js @@ -0,0 +1,1141 @@ +// eslint-ignore-next-line no-var +var ReloopReady = {}; + +/// The controller only offers Low, High and Gain Knobs, +/// settings this to true will remap them to control Low, Mid, High instead. +ReloopReady.threeBandEQ = false; + +ReloopReady.backlightButtons = true; + +/// scratch parameter, change to your liking +ReloopReady.scratchParams = { + alpha: 1/8, + beta: (1/8)/32, +}; + +/// On my hardware unit, the tempo faders have such a cheap build-quality +/// that the notches in the center are not actually in the middle +/// If you find your notches are offset too, do this: +/// Load this mapping in mixxx using `--developer` mode. +/// Then move the tempo fader into the notch at the position where +/// the tempo should then be the true tempo (+/- 0). Open the developer tools +/// and read the `rate` value of the channel. If its already 0.5 you should be +/// good, if its not that, copy the value here and the fader will be adjusted +/// for you. +ReloopReady.tempoFaderMiddles = [0.5, 0.5]; + + +/** + * Misc. midi notes + * + * Note 0x31 firmware fader-start feature. Note 0x7F if volume fader is non-zero + */ + + +/** + * Color: + * The Reloop ready uses the entire 7-bit midi range for color. + * It encodes it in a simple to understand (but inefficient in regards to + * colors perceived by us) scheme: + * It uses the lower 6-bit for RGB colors (each channel having the unbelievable + * resolution of 2-bit), while the 7th-bit is used to switch between two + * brightness/intensity modes (with the 7th-bit set, meaning high brightness). + * + * |7654321| + * +-------+ + * |IRRGGBB| + * Example: the fullest, brightest red would have I set and the red channel on + * maximum: 0b1110000 + * + * Using that knowledge, we can extrapolate a 24-bit color from any 7-bit color: + */ + +ReloopReady.midiToFullColor = color => { + + const b = (color & 0b00000011) << 6; + const g = (color & 0b00001100) << 4; + const r = (color & 0b00110000) << 2; + const i = (color & 0b01000000) >> 6; + + if (i === 0) { + // half the RGB intensity + r >> 0b1; + g >> 0b1; + b >> 0b1; + } + return (r << 16) | (g << 8) | b; +}; + +ReloopReady.padColorPalette = { + Off: 0x000000, + Red: 0xFF0000, + Green: 0x00FF00, + Blue: 0x0000FF, + Yellow: 0xFFFF00, + Cyan: 0x007FFF, + Purple: 0xFF00FF, + White: 0xFFFFFF, +}; + +ReloopReady.requestFirmwareVersionSysex = [0xF0, 0x00, 0x20, 0x7F, 0x19]; + +// dim color by stripping the "intensity bit" +ReloopReady.dimColor = color => color & 0b00111111; + +ReloopReady.noopInputHandler = function(_channel, _control, _value, _status, _group) {}; + +// IIFE for not leaking "private" variables +(() => { + + // rewrite the following without lodash + const fullColorToMidiColorMap = _(_.range(0x00, 0x80)) + .keyBy() + .mapKeys(ReloopReady.midiToFullColor) + .value(); + + ReloopReady.padColorMapper = new ColorMapper(fullColorToMidiColorMap); + + // transform padColorPalette to low-res midi approximations: + + ReloopReady.padColorPalette = _.mapValues(ReloopReady.padColorPalette, color => { + const litColor = ReloopReady.padColorMapper.getValueForNearestColor(color); + const dimColor = ReloopReady.dimColor(litColor); + return { + lit: litColor, + dim: dimColor, + }; + }); +})(); + +components.Button.prototype.sendShifted = true; +components.Button.prototype.shiftControl = true; +// shift control offset depends on concrete control + +components.Encoder.prototype.inValueScale = function(value) { + return value < 0x40 ? value : value - 0x80; +}; + +ReloopReady.singleColorLED = { + off: 0x00, + dim: 0x01, + lit: 0x7F, // anything between 0x02 and 0x7F is fully lit +}; + +// make Buttons backlight be default based on user-setting +components.Button.prototype.off = ReloopReady.backlightButtons ? ReloopReady.singleColorLED.dim : ReloopReady.singleColorLED.off; +components.Button.prototype.on = ReloopReady.singleColorLED.lit; + +/** + * creates an this.isPress guarded input handler, assumes to be called by components.Button or subclasses thereof + * @param {(value: number) => void} func callback that is called on ButtonDown + * @returns {(channel: number, control: number, value: number, status: number, group: string) => void} XML input handler + */ +ReloopReady.makeButtonDownInputHandler = function(func) { + return function(channel, control, value, status, _group) { + const isPress = this.isPress(channel, control, value, status); + this.output(isPress); + if (!isPress) { + return; + } + func.call(this, value); + }; +}; + +/** + * Handle incoming sysex message + * @param {Array} data sysex data received from the controller + */ +ReloopReady.incomingData = data => { + const arrayStartsWith = (arr, pattern) => pattern.every((elem, i) => elem === arr[i]); + if (arrayStartsWith(data, ReloopReady.requestFirmwareVersionSysex)) { + const firmwareVersion = data.slice(ReloopReady.requestFirmwareVersionSysex.length, data.length - 1); + // no idea if that's actually the format used for the firmware version, + // its just 4 bytes (7-bit cuz midi) for me. + console.log(`firmware version: ${firmwareVersion.join(".")}`); + return; + } + console.warn(`unregonized incoming sysex data: ${data}`); +}; + +ReloopReady.init = function() { + + this.components = new components.ComponentContainer(); + + this.components.leftDeck = new ReloopReady.Deck(0); + this.components.rightDeck = new ReloopReady.Deck(1); + this.components.leftChannel = new ReloopReady.Channel(0); + this.components.rightChannel = new ReloopReady.Channel(1); + + this.components.crossfader = new components.Pot({ + midi: [0xBE, 0x08], + group: "[Master]", + inKey: "crossfader" + }); + + this.components.masterVol = new components.Pot({ + midi: [0xBE, 0x0A], + group: "[Master]", + inKey: "gain" + }); + + this.components.cueMix = new components.Pot({ + midi: [0xBE, 0x0D], + group: "[Master]", + inKey: "headMix" + }); + + this.components.cueVol = new components.Pot({ + midi: [0xBE, 0x0C], + group: "[Master]", + inKey: "headGain" + }); + + this.components.browse = new components.ComponentContainer({ + button: new components.Button({ + midi: [0x9E, 0x06], + unshift: function() { + this.group = "[Library]"; + this.inKey = "GoToItem"; + this.type = components.Button.prototype.types.push; + }, + shift: function() { + this.group = "[Master]"; + this.inKey = "maximize_library"; + this.type = components.Button.prototype.types.toggle; + } + }), + knob: new components.Encoder({ + midi: [0xBE, 0x00], + group: "[Library]", + unshift: function() { + this.inKey = "MoveVertical"; + }, + shift: function() { + this.inKey = "MoveFocus"; + } + }) + }); + + const shiftableComponents = this.components; + + this.shift = new components.Button({ + midi: [0x9E, 0x00], + input: function(channel, control, value, status, _group) { + shiftableComponents.forEachComponent(comp => comp.disconnect()); + if (this.isPress(channel, control, value, status)) { + shiftableComponents.shift(); + } else { + shiftableComponents.unshift(); + } + shiftableComponents.forEachComponent(comp => { + comp.connect(); + comp.trigger(); + }); + }, + }); + + // request controls status per standard serato sysex + midi.sendSysexMsg([0xF0, 0x00, 0x20, 0x7F, 0x00, 0xF7]); + // request firmware version + midi.sendSysexMsg(ReloopReady.requestFirmwareVersionSysex.concat([0xF7])); +}; + +ReloopReady.shutdown = () => { + ReloopReady.components.shutdown(); +}; + + +ReloopReady.Channel = function(index) { + const channel = index + 1; + const group = `[Channel${channel}]`; + const eqGroup = `[EqualizerRack1_${group}_Effect1]`; + + if (ReloopReady.threeBandEQ) { + this.knob1 = new components.Pot({ + midi: [0xB0 + index, 0x16], + group: eqGroup, + inKey: "parameter3" + }); + this.knob2 = new components.Pot({ + midi: [0xB0 + index, 0x17], + group: eqGroup, + inKey: "parameter2" + }); + } else { + this.knob1 = new components.Pot({ + midi: [0xB0 + index, 0x16], + inKey: "pregain" + }); + + this.knob2 = new components.Pot({ + midi: [0xB0 + index, 0x17], + group: eqGroup, + inKey: "parameter3" + }); + } + + this.knob3 = new components.Pot({ + midi: [0xB0 + index, 0x19], + group: eqGroup, + inKey: "parameter1" + }); + + + this.filter = new components.Pot({ + midi: [0xB0 + index, 0x1A], + group: `[QuickEffectRack1_${group}]`, + inKey: "super1" + }); + + this.rate = new components.Pot({ + midi: [0xB0 + index, 0x09], // MSB control: 0x3F + inKey: "rate", + // tempo fader response: top ("-") @ 0, bottom ("+") @ 1023 + max: 1023, + invert: true, + // see explanation of ReloopReady.tempoFaderMiddles for info + inValueScale: function(value) { + const middle = (this.max + 1) * ReloopReady.tempoFaderMiddles[index]; + return script.absoluteNonLinInverse(value, 0, middle, this.max, 0, 1); + } + }); + + // TODO this seems to have soft-takeover enabled sometimes?! + this.volume = new components.Pot({ + midi: [0xB0 + index, 0x1C], + inKey: "volume", + }); + + this.pfl = new components.Button({ + midi: [0x90 + index, 0x1B], + shiftOffset: -0xD, + key: "pfl", + type: components.Button.prototype.types.toggle, + }); + + this.load = new components.Button({ + midi: [0x9E, 0x02 + index], + shiftOffset: 0x0D, + shift: function() { + this.inKey = "eject"; + this.outKey = this.inKey; + }, + unshift: function() { + this.inKey = "LoadSelectedTrack"; + this.outKey = this.inKey; + } + }); + + + this.reconnectComponents(c => { + if (c.group === undefined) { + c.group = group; + } + }); +}; + +ReloopReady.Channel.prototype = new components.ComponentContainer(); + +ReloopReady.FxUnit = function(index) { + + const midiOn = 0x98 + index; + const channel = index + 1; + + this.level = new components.Pot({ + midi: [0xB8 + index, 0x00], + group: `[EffectRack1_EffectUnit${channel}]`, + inKey: "super1", + }); + this.fxButtons = Array.from([0x00, 0x01, 0x02, 0x03], control => + new components.Button({ + midi: [midiOn, control], + shiftOffset: 0xA, + group: `[EffectRack1_EffectUnit${channel}_Effect${control + 1}]`, + unshift: function() { + this.inKey = "enabled"; + this.outKey = this.inKey; + this.type = components.Button.prototype.types.toggle; + }, + shift: function() { + this.inKey = "next_effect"; + this.outKey = "loaded"; + this.type = components.Button.prototype.types.push; + } + }) + ); + + this.loop = { + knob: new components.Encoder({ + midi: [0xB4 + index, 0x03], + group: `[Channel${channel}]`, + input: function(_channel, _control, value, _status, _group) { + engine.setValue(this.group, value > 0x40 ? "loop_halve" : "loop_double", 1); + } + }), + button: new components.Button({ + midi: [0x94 + index, 0x40], // shifted: [0x98 + index, 0x04] + group: `[Channel${channel}]`, + input: function(channel, control, value, status, _group) { + if (this.isPress(channel, control, value, status)) { + if (this.shifted) { + // non standard behavior + script.bpm.tapButton(index); + } else { + // TODO: For 2.4 using plain beatloop_activate + // is sufficient, see #4328 + if (engine.getValue(this.group, "loop_enabled")) { + script.triggerControl(this.group, "reloop_toggle"); + } else { + script.triggerControl(this.group, "beatloop_activate"); + } + } + } + }, + }) + }; +}; + +ReloopReady.FxUnit.prototype = new components.ComponentContainer(); + + +ReloopReady.PadMode = function(obj) { + components.ComponentContainer.call(this, obj); + // interpret this an interface definition + // we fill this array to avoid the issues with iteration methods and sparse arrays + this.pads = Array.from(Array(8), (_, i) => 0x14 + i); + this.parameterLeft = new components.Button(); + this.parameterRight = new components.Button(); + this.shutdown = () => { + // we want the pads to be completely off when shutdown, not dimmed + this.pads.forEach(pad => pad.send(0x00)); + this.parameterLeft.shutdown(); + this.parameterRight.shutdown(); + }; +}; + +ReloopReady.PadMode.prototype = Object.create(components.ComponentContainer.prototype); + +ReloopReady.HotcuePadMode = function(index) { + ReloopReady.PadMode.call(this); + // can't use map on sparse arrays, so we use.from + this.pads = this.pads.map((control, i) => + new components.HotcueButton({ + midi: [0x94 + index, control], + group: `[Channel${index + 1}]`, + shiftOffset: 0x8, + off: 0x00, + number: i + 1, + colorMapper: ReloopReady.padColorMapper, + outConnect: false, + }) + ); +}; + +ReloopReady.HotcuePadMode.prototype = Object.create(ReloopReady.PadMode.prototype); + +ReloopReady.AutoLoopPadMode = function(index) { + ReloopReady.PadMode.call(this); + + this.currentLoopSizeExp = -2; + const theContainer = this; + + const clampLoopSizeExp = loopSizeExp => _.clamp(loopSizeExp, -5, -2); + + this.setLoopSizes = loopSizeExp => { + _(_.range(loopSizeExp, loopSizeExp + 8)) + .map(function(exp) { return Math.pow(2, exp); }) + .zip(this.pads) + .forEach(([size, pad]) => { + pad.inKey = `beatloop_${size}_toggle`; + pad.outKey = `beatloop_${size}_enabled`; + }); + }; + + for (let i = 0; i < this.pads.length; i++) { + this.pads[i] = new components.Button({ + midi: [0x94 + index, 0x14 + i], + group: `[Channel${index + 1}]`, + shiftOffset: 0x8, + off: ReloopReady.padColorPalette.Red.dim, + on: ReloopReady.padColorPalette.Red.lit, + outConnect: false, + }); + } + + const makeParameterInputHandler = loopSizeChangeAmount => { + return function(channel, control, value, status, _group) { + const pressed = this.isPress(channel, control, value, status); + if (pressed) { + const newLoopSize = clampLoopSizeExp(theContainer.currentLoopSizeExp + loopSizeChangeAmount); + if (newLoopSize !== theContainer.currentLoopSizeExp) { + theContainer.currentLoopSizeExp = newLoopSize; + theContainer.setLoopSizes(newLoopSize); + theContainer.reconnectComponents(); + } + } + this.output(pressed); + }; + }; + + this.parameterLeft = new components.Button({ + midi: [0x94 + index, 0x28], //shifted control: 0x2A + shiftOffset: 0x2, + input: makeParameterInputHandler(-1), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + index, 0x29], //shifted control: 0x2B + shiftOffset: 0x2, + input: makeParameterInputHandler(1), + }); + + this.setLoopSizes(this.currentLoopSizeExp); +}; + +ReloopReady.AutoLoopPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); + +ReloopReady.ManualLoopPadMode = function(index) { + ReloopReady.PadMode.call(this); + + let loopMoveBeats = 4; + const group = `[Channel${index + 1}]`; + + this.pads[0] = new components.Button({ + key: "loop_in", + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + }); + this.pads[1] = new components.Button({ + key: "loop_out", + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + }); + this.pads[2] = new components.Button({ + inKey: "reloop_toggle", + outKey: "loop_enabled", + on: ReloopReady.padColorPalette.Red.lit, + off: ReloopReady.padColorPalette.Red.dim, + }); + this.pads[3] = new components.Button({ + key: "loop_in_goto", + on: ReloopReady.padColorPalette.Red.lit, + off: ReloopReady.padColorPalette.Red.dim, + }); + this.pads[4] = new components.Button({ + key: "loop_halve", + on: ReloopReady.padColorPalette.Green.lit, + off: ReloopReady.padColorPalette.Green.dim, + }); + this.pads[5] = new components.Button({ + key: "loop_double", + on: ReloopReady.padColorPalette.Green.lit, + off: ReloopReady.padColorPalette.Green.dim, + }); + this.pads[6] = new components.Button({ + inKey: "loop_move", + on: ReloopReady.padColorPalette.Cyan.lit, + off: ReloopReady.padColorPalette.Cyan.dim, + unshift: function() { + this.input = ReloopReady.makeButtonDownInputHandler(function() { + this.inSetValue(-loopMoveBeats); + }); + }, + shift: function() { + this.input = ReloopReady.makeButtonDownInputHandler(function() { + this.inSetValue(-engine.getValue(this.group, "loop_size")); + }); + }, + trigger: function() { + this.output(); + }, + }); + this.pads[7] = new components.Button({ + inKey: "loop_move", + on: ReloopReady.padColorPalette.Cyan.lit, + off: ReloopReady.padColorPalette.Cyan.dim, + unshift: function() { + this.input = ReloopReady.makeButtonDownInputHandler(function() { + this.inSetValue(loopMoveBeats); + }); + }, + shift: function() { + this.input = ReloopReady.makeButtonDownInputHandler(function() { + this.inSetValue(engine.getValue(this.group, "loop_size")); + }); + }, + trigger: function() { + this.output(); + }, + }); + + this.pads.forEach((pad, i) => pad.midi = [0x94 + index, 0x14 + i]); + + this.parameterLeft = new components.Button({ + midi: [0x94 + index, 0x28], + shiftOffset: 0x2, + input: ReloopReady.makeButtonDownInputHandler(function() { + loopMoveBeats *= 0.5; + }), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + index, 0x29], + shiftOffset: 0x2, + input: ReloopReady.makeButtonDownInputHandler(function() { + loopMoveBeats *= 2; + }), + }); + + this.reconnectComponents(c => { + if (c.group === undefined) { + c.group = group; + } + }); +}; +ReloopReady.ManualLoopPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); + + +ReloopReady.SamplerPadMode = function(index) { + ReloopReady.PadMode.call(this, index); + + // var baseOffset = index * 8; + + this.pads = this.pads.map((_, i) => + new components.SamplerButton({ + midi: [0x94 + index, 0x14 + i], + // number: baseOffset + i + 1, + number: i + 1, + shiftOffset: 0x8, + off: 0x00, + outConnect: false, + }) + ); +}; +ReloopReady.SamplerPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); + + +ReloopReady.LoopRollPadMode = function(index) { + ReloopReady.PadMode.call(this); + + this.currentLoopSizeExp = -2; + const theContainer = this; + + const clampLoopSizeExp = loopSizeExp => _.clamp(loopSizeExp, -5, -2); + + this.setLoopSizes = loopSizeExp => { + _(_.range(loopSizeExp, loopSizeExp + 8)) + .map(function(exp) { return Math.pow(2, exp); }) + .zip(this.pads) + .forEach(([size, pad]) => { + pad.inKey = `beatlooproll_${size}_activate`; + pad.outKey = `beatloop_${size}_enabled`; + }); + }; + + for (let i = 0; i < this.pads.length; i++) { + this.pads[i] = new components.Button({ + midi: [0x94 + index, 0x14 + i], + group: `[Channel${index + 1}]`, + shiftOffset: 0x8, + off: ReloopReady.padColorPalette.Green.dim, + on: ReloopReady.padColorPalette.Green.lit, + outConnect: false, + }); + } + + const makeParameterInputHandler = loopSizeChangeAmount => + ReloopReady.makeButtonDownInputHandler(function() { + const newLoopSize = clampLoopSizeExp(theContainer.currentLoopSizeExp + loopSizeChangeAmount); + if (newLoopSize !== theContainer.currentLoopSizeExp) { + theContainer.currentLoopSizeExp = newLoopSize; + theContainer.setLoopSizes(newLoopSize); + theContainer.reconnectComponents(); + } + }); + + this.parameterLeft = new components.Button({ + midi: [0x94 + index, 0x28], + shiftOffset: 0x2, + input: makeParameterInputHandler(-1), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + index, 0x29], + shiftOffset: 0x2, + input: makeParameterInputHandler(1), + }); + + this.setLoopSizes(this.currentLoopSizeExp); +}; + +ReloopReady.LoopRollPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); + + +// Pitch Play mode taken from Roland DJ 505 mapping. +ReloopReady.Pitch = function(index) { + components.ComponentContainer.call(this); + + const PitchPlayRange = { + UP: 0, + MID: 1, + DOWN: 2, + }; + + //this.ledControl = DJ505.PadMode.SAMPLER; + const color = ReloopReady.padColorPalette.Purple; + let cuepoint = 1; + let range = PitchPlayRange.MID; + const theContainer = this; + + this.PerformancePad = function(n) { + this.midi = [0x94 + index, 0x14 + n]; + this.number = n + 1; + this.on = ReloopReady.padColorPalette.Purple.dim; + this.colorMapper = ReloopReady.padColorMapper; + this.colorKey = `hotcue_${this.number}_color`; + components.Button.call(this); + }; + this.PerformancePad.prototype = new components.Button({ + shiftOffset: 8, + group: `[Channel${index + 1}]`, + outConnect: false, + off: ReloopReady.padColorPalette.Off.lit, + outputColor: function(colorCode) { + // For colored hotcues (shifted only) + const midiColor = this.colorMapper.getValueForNearestColor(colorCode); + this.send((cuepoint === this.number) ? midiColor : ReloopReady.dimColor(midiColor)); + }, + unshift: function() { + this.outKey = "pitch_adjust"; + this.output = function(_value, _group, _control) { + let midiColor = color.dim; + if ((range === PitchPlayRange.UP && this.number === 5) || + (range === PitchPlayRange.MID && this.number === 1) || + (range === PitchPlayRange.DOWN && this.number === 4)) { + midiColor = ReloopReady.padColorPalette.White.lit; + } + this.send(midiColor); + }; + this.input = function(_channel, _control, value, _status, _group) { + const pitchAdjust = (function() { + switch (range) { + case PitchPlayRange.UP: + return this.number + ((this.number <= 4) ? 4 : -5); + case PitchPlayRange.MID: + return this.number - ((this.number <= 4) ? 1 : 9); + case PitchPlayRange.DOWN: + return this.number - ((this.number <= 4) ? 4 : 12); + } + }).call(this); + engine.setValue(this.group, "pitch_adjust", pitchAdjust); + engine.setValue(this.group, `hotcue_${cuepoint}_activate`, value); + }; + this.connect = function() { + components.Button.prototype.connect.call(this); // call parent connect + + if (this.connections[1] !== undefined) { + // Necessary, since trigger() apparently also triggers disconnected connections + this.connections.pop(); + } + }; + if (this.connections[0] !== undefined) { + this.disconnect(); + this.connect(); + this.trigger(); + } + }, + shift: function() { + this.outKey = `hotcue_${this.number}_enabled`; + this.output = function(value, _group, _control) { + const outval = this.outValueScale(value); + if (this.colorKey !== undefined && outval !== this.off) { + this.outputColor(engine.getValue(this.group, this.colorKey)); + } else { + this.send(ReloopReady.padColorPalette.Off.lit); + } + }; + this.input = function(_channel, _control, value, _status, _group) { + if (value > 0 && cuepoint !== this.number && engine.getValue(this.group, `hotcue_${this.number}_enabled`)) { + const previousCuepoint = cuepoint; + cuepoint = this.number; + theContainer.pads[previousCuepoint - 1].trigger(); + this.outputColor(engine.getValue(this.group, this.colorKey)); + } + }; + this.connect = function() { + components.Button.prototype.connect.call(this); // call parent connect + if (undefined !== this.group && this.colorKey !== undefined) { + this.connections[1] = engine.makeConnection(this.group, this.colorKey, function(id) { + if (engine.getValue(this.group, this.outKey)) { + this.outputColor(id); + } + }); + } + }; + if (this.connections[0] !== undefined) { + this.disconnect(); + this.connect(); + this.trigger(); + } + }, + }); + this.pads = new components.ComponentContainer(); + for (let n = 0; n <= 7; n++) { + this.pads[n] = new this.PerformancePad(n); + } + + this.parameterLeft = new components.Button({ + midi: [0x94 + index, 0x28], + shiftOffset: 0x2, + input: ReloopReady.makeButtonDownInputHandler(function() { + if (range === PitchPlayRange.UP) { + range = PitchPlayRange.MID; + } else if (range === PitchPlayRange.MID) { + range = PitchPlayRange.DOWN; + } else { + range = PitchPlayRange.UP; + } + theContainer.forEachComponent(c => c.trigger()); + }), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + index, 0x29], + shiftOffset: 0x2, + input: ReloopReady.makeButtonDownInputHandler(function() { + if (range === PitchPlayRange.UP) { + range = PitchPlayRange.DOWN; + } else if (range === PitchPlayRange.MID) { + range = PitchPlayRange.UP; + } else { + range = PitchPlayRange.MID; + } + theContainer.forEachComponent(c => c.trigger()); + }), + }); +}; + +ReloopReady.Pitch.prototype = Object.create(ReloopReady.PadMode.prototype); + + +// There is no such thing as a scratch bank in Mixxx so I'm repurpusing this +// PadMode for beatjumping. +ReloopReady.ScratchBankPadMode = function(index) { + ReloopReady.PadMode.call(this); + + this.currentJumpSizeExp = -2; + const theContainer = this; + + const clampJumpSizeExp = jumpSizeExp => _.clamp(jumpSizeExp, -5, 6); + + this.setjumpSizeExp = jumpSizeExp => { + const middle = this.pads.length / 2; + let jumpsize = Math.pow(2, jumpSizeExp); + for (let i = 0; i < middle; ++i) { + const padTop = this.pads[i]; + padTop.inKey = `beatjump_${jumpsize}_forward`; + padTop.outKey = padTop.inKey; + const padBot = this.pads[i + middle]; + padBot.inKey = `beatjump_${jumpsize}_backward`; + padBot.outKey = padBot.inKey; + jumpsize *= 2; + } + }; + + + for (let i = 0; i < this.pads.length; i++) { + this.pads[i] = new components.Button({ + midi: [0x94 + index, 0x14 + i], + group: `[Channel${index + 1}]`, + shiftOffset: 0x8, + off: ReloopReady.padColorPalette.Cyan.dim, + on: ReloopReady.padColorPalette.Cyan.lit, + outConnect: false, + }); + } + + const makeParameterInputHandler = jumpSizeChangeAmount => + ReloopReady.makeButtonDownInputHandler(function() { + const newJumpSize = clampJumpSizeExp(theContainer.currentJumpSizeExp + jumpSizeChangeAmount); + if (newJumpSize !== theContainer.currentJumpSizeExp) { + theContainer.currentJumpSizeExp = newJumpSize; + theContainer.setjumpSizeExp(newJumpSize); + theContainer.reconnectComponents(); + } + }); + + this.parameterLeft = new components.Button({ + midi: [0x94 + index, 0x28], + shiftOffset: 0x2, + input: makeParameterInputHandler(-1), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + index, 0x29], + shiftOffset: 0x2, + input: makeParameterInputHandler(1), + }); + + this.setjumpSizeExp(this.currentJumpSizeExp); +}; + +ReloopReady.ScratchBankPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); + +ReloopReady.BeatGridPadMode = function(index) { + ReloopReady.PadMode.call(this); + + const group = `[Channel${index + 1}]`; + + this.pads[0] = new components.Button({ + key: "beats_translate_earlier", + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + }); + this.pads[1] = new components.Button({ + key: "beats_translate_later", + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + }); + this.pads[2] = new components.Button({ + key: "beats_adjust_faster", + on: ReloopReady.padColorPalette.Red.lit, + off: ReloopReady.padColorPalette.Red.dim, + trigger: function() { + this.output(); + }, + }); + this.pads[3] = new components.Button({ + key: "beats_adjust_slower", + on: ReloopReady.padColorPalette.Red.lit, + off: ReloopReady.padColorPalette.Red.dim, + trigger: function() { + this.output(); + }, + }); + this.pads[4] = new components.Button({ + key: "shift_cues_earlier", + on: ReloopReady.padColorPalette.Green.lit, + off: ReloopReady.padColorPalette.Green.dim, + trigger: function() { + this.output(); + }, + }); + this.pads[5] = new components.Button({ + key: "shift_cues_later", + on: ReloopReady.padColorPalette.Green.dim, + off: ReloopReady.padColorPalette.Green.dim, + trigger: function() { + this.output(); + }, + }); + this.pads[6] = new components.Button({ + key: "bpm_tap", + on: ReloopReady.padColorPalette.Cyan.dim, + off: ReloopReady.padColorPalette.Cyan.dim, + }); + this.pads[7] = new components.Button({ + key: "beats_translate_curpos", + on: ReloopReady.padColorPalette.Cyan.lit, + off: ReloopReady.padColorPalette.Cyan.dim, + }); + + this.pads.forEach((pad, i) => pad.midi = [0x94 + index, 0x14 + i]); + + this.parameterLeft = new components.Button({ + midi: [0x94 + index, 0x28], + shiftOffset: 0x2 + }); + this.parameterRight = new components.Button({ + midi: [0x94 + index, 0x29], + shiftOffset: 0x2 + }); + + this.reconnectComponents(c => { + if (c.group === undefined) { + c.group = group; + } + }); +}; +ReloopReady.BeatGridPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); + +// Ordering of array elements determines layout of pad mode selectors. +ReloopReady.controlPadModeAssoc = [ + {pos: 0, control: 0x00, mode: ReloopReady.HotcuePadMode}, + {pos: 1, control: 0x05, mode: ReloopReady.AutoLoopPadMode}, + {pos: 2, control: 0x0E, mode: ReloopReady.ManualLoopPadMode}, + {pos: 3, control: 0x0B, mode: ReloopReady.SamplerPadMode}, + {pos: 4, control: 0x0F, mode: ReloopReady.Pitch}, + {pos: 5, control: 0x09, mode: ReloopReady.ScratchBankPadMode}, + {pos: 6, control: 0x08, mode: ReloopReady.LoopRollPadMode}, + {pos: 7, control: 0x12, mode: ReloopReady.BeatGridPadMode}, +]; + +ReloopReady.PadContainer = function(index) { + + // parent constructor is called later + + components.ComponentContainer.call(this); + + // construct instance of each mode per Container + const padModeInstances = ReloopReady.controlPadModeAssoc.map(obj => { + const modeInstance = new (obj.mode)(index); + // make sure no multiple components have "ownership" over each pad + modeInstance.forEachComponent(c => c.disconnect()); + return { + pos: obj.pos, + control: obj.control, + modeInstance: modeInstance, + }; + }); + + this.currentLayer = padModeInstances[0].modeInstance; + this.currentLayer.reconnectComponents(); + + const applyLayer = layer => { + if (this.currentLayer === layer) { + return; + } + + this.currentLayer.forEachComponent(c => c.disconnect()); + if (this.isShifted) { + layer.shift(); + } else { + layer.unshift(); + } + + layer.forEachComponent(c => { + c.connect(); + c.trigger(); + }); + + this.currentLayer = layer; + + this.padModeSelectors.forEach(selector => selector.trigger()); + }; + + // factory/HO function for creating input handlers that change the padmode + // this expects to be used as for creating the input handler of a + // components.Button + const makePadModeInputHandler = layer => + (layer === undefined) ? + ReloopReady.noopInputHandler : + function(channel, control, value, status, _group) { + if (!this.isPress(channel, control, value, status)) { + return; + } + applyLayer(layer); + }; + + const thisContainer = this; + + // create physical buttons for changing the layers + this.padModeSelectors = Array(8); + padModeInstances.forEach(obj => { + thisContainer.padModeSelectors[obj.pos] = new components.Button({ + midi: [0x94 + index, obj.control], + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + input: makePadModeInputHandler(obj.modeInstance), + trigger: function() { + this.output(thisContainer.currentLayer === obj.modeInstance); + } + }); + }); + this.padModeSelectors = this.padModeSelectors.map(obj => + obj === undefined ? new components.Button({}) : obj + ); + + // button can not be controlled from software. + // This component instance just serves as input handler for debugging. + this.modeButton = new components.Button({ + midi: [index, 0x20], // shifted: [0x0C + index, 0x11], shifted led is controllable, unshifted is not. + input: ReloopReady.noopInputHandler + }); +}; + +ReloopReady.PadContainer.prototype = Object.create(components.ComponentContainer.prototype); + +ReloopReady.Deck = function(index) { + + const channel = index + 1; + + components.Deck.call(this, channel); + + const thisDeck = this; + const midiOn = 0x90 + index; + + + this.play = new components.PlayButton({ + midi: [midiOn, 0x00], + shiftOffset: 0x10, + shift: function() { + // match behavior labelled on hardware + this.inKey = "reverseroll"; + // todo fix interaction with shift button + }, + }); + + // needs matching cuemode to be used + this.cue = new components.CueButton({ + midi: [midiOn, 0x01], + shiftOffset: 0x04, + }); + + this.sync = new components.SyncButton({ + midi: [midiOn, 0x02], + shiftOffset: 0x1, + }); // TODO investigate whether custom behavior is required to match controller + + this.vinyl = new components.Button({ + midi: [midiOn, 0x0F], + shiftOffset: -0x08, + type: components.Button.prototype.types.toggle, + shift: function() { + this.inKey = "slip_enabled"; + this.outKey = this.inKey; + this.input = components.Button.prototype.input; + }, + unshift: function() { + this.input = function(channel, control, value, status, _group) { + if (this.isPress(channel, control, value, status)) { + thisDeck.jog.vinylMode = ! thisDeck.jog.vinylMode; + this.output(thisDeck.jog.vinylMode); + } + }; + }, + trigger: function() { + this.output(thisDeck.jog.vinylMode); + } + }); + + this.jog = new components.JogWheelBasic({ + // midiTouch: [0x90 + index, 0x06] + // midiWheel: [0xB0 + index, 0x06] + alpha: 1/8, + deck: channel, + wheelResolution: 300, + }); + + + this.keylock = new components.Button({ + midi: [midiOn, 0x0D], + shiftOffset: 0x1C, + shift: function() { + this.type = components.Button.prototype.types.push; + this.inKey = "sync_key"; + this.outKey = this.inKey; + }, + unshift: function() { + this.type = components.Button.prototype.types.toggle; + this.inKey = "keylock"; + this.outKey = this.inKey; + } + }); + + this.fxUnit = new ReloopReady.FxUnit(index); + + this.padUnit = new ReloopReady.PadContainer(index); + + this.reconnectComponents(function(c) { + if (c.group === undefined) { + c.group = thisDeck.currentDeck; + } + }); +}; + +ReloopReady.Deck.prototype = new components.Deck(); From ae89454aa1c3bb5c0b51897480f5df762527f4de Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Sun, 18 Feb 2024 21:48:05 +0100 Subject: [PATCH 02/18] various improvements from review and feedback. ES6 classes, fix pitch-play with loopcues removed unused vars deduplicate loop-related padmodes query firmware version softakeover on volume faders remove pre-2.4 workarounds fix small looprange --- res/controllers/Reloop Ready.midi.xml | 482 +++--- res/controllers/Reloop-Ready-scripts.js | 1821 ++++++++++++----------- 2 files changed, 1164 insertions(+), 1139 deletions(-) diff --git a/res/controllers/Reloop Ready.midi.xml b/res/controllers/Reloop Ready.midi.xml index c58c7e55c77..235cf5206ca 100644 --- a/res/controllers/Reloop Ready.midi.xml +++ b/res/controllers/Reloop Ready.midi.xml @@ -1,24 +1,24 @@ - + Reloop Ready Swiftb0y - Almost feature-complete mapping of the Reloop Ready hardware. Development kindly sponsored by Samgarr + feature-complete mapping of the Reloop Ready hardware. Development kindly sponsored by Samgarr - + - + - ReloopReady.components.leftDeck.jog.inputTouch + ReloopReadyInstance.components.leftDeck.jog.inputTouch 0x90 0x06 @@ -26,7 +26,7 @@ - ReloopReady.components.leftDeck.jog.inputTouch + ReloopReadyInstance.components.leftDeck.jog.inputTouch 0x80 0x06 @@ -35,7 +35,7 @@ - ReloopReady.components.leftDeck.jog.inputWheel + ReloopReadyInstance.components.leftDeck.jog.inputWheel 0xB0 0x06 @@ -45,7 +45,7 @@ - ReloopReady.components.rightDeck.jog.inputTouch + ReloopReadyInstance.components.rightDeck.jog.inputTouch 0x91 0x06 @@ -53,7 +53,7 @@ - ReloopReady.components.rightDeck.jog.inputTouch + ReloopReadyInstance.components.rightDeck.jog.inputTouch 0x81 0x06 @@ -62,7 +62,7 @@ - ReloopReady.components.rightDeck.jog.inputWheel + ReloopReadyInstance.components.rightDeck.jog.inputWheel 0xB1 0x06 @@ -70,10 +70,30 @@ + + + ReloopReadyInstance.components.leftDeck.jog.inputWheel + 0xB0 + 0x2B + + + + + + + + ReloopReadyInstance.components.rightDeck.jog.inputWheel + 0xB1 + 0x2B + + + + + - ReloopReady.components.leftChannel.knob1.input + ReloopReadyInstance.components.leftChannel.knob1.input 0xB0 0x16 @@ -82,7 +102,7 @@ - ReloopReady.components.leftChannel.knob2.input + ReloopReadyInstance.components.leftChannel.knob2.input 0xB0 0x17 @@ -91,7 +111,7 @@ - ReloopReady.components.leftChannel.knob3.input + ReloopReadyInstance.components.leftChannel.knob3.input 0xB0 0x19 @@ -100,7 +120,7 @@ - ReloopReady.components.leftChannel.filter.input + ReloopReadyInstance.components.leftChannel.filter.input 0xB0 0x1A @@ -109,7 +129,7 @@ - ReloopReady.components.leftChannel.volume.input + ReloopReadyInstance.components.leftChannel.volume.input 0xB0 0x1C @@ -118,7 +138,7 @@ - ReloopReady.components.leftChannel.rate.inputMSB + ReloopReadyInstance.components.leftChannel.rate.inputMSB 0xB0 0x09 @@ -127,7 +147,7 @@ - ReloopReady.components.leftChannel.rate.inputLSB + ReloopReadyInstance.components.leftChannel.rate.inputLSB 0xB0 0x3F @@ -137,7 +157,7 @@ - ReloopReady.components.leftChannel.load.input + ReloopReadyInstance.components.leftChannel.load.input 0x9E 0x02 @@ -146,7 +166,7 @@ - ReloopReady.components.leftChannel.load.input + ReloopReadyInstance.components.leftChannel.load.input 0x8E 0x02 @@ -156,7 +176,7 @@ - ReloopReady.components.leftChannel.pfl.input + ReloopReadyInstance.components.leftChannel.pfl.input 0x90 0x1B @@ -165,7 +185,7 @@ - ReloopReady.components.leftChannel.pfl.input + ReloopReadyInstance.components.leftChannel.pfl.input 0x80 0x1B @@ -176,7 +196,7 @@ - ReloopReady.components.rightChannel.knob1.input + ReloopReadyInstance.components.rightChannel.knob1.input 0xB1 0x16 @@ -185,7 +205,7 @@ - ReloopReady.components.rightChannel.knob2.input + ReloopReadyInstance.components.rightChannel.knob2.input 0xB1 0x17 @@ -194,7 +214,7 @@ - ReloopReady.components.rightChannel.knob3.input + ReloopReadyInstance.components.rightChannel.knob3.input 0xB1 0x19 @@ -203,7 +223,7 @@ - ReloopReady.components.rightChannel.filter.input + ReloopReadyInstance.components.rightChannel.filter.input 0xB1 0x1A @@ -212,7 +232,7 @@ - ReloopReady.components.rightChannel.volume.input + ReloopReadyInstance.components.rightChannel.volume.input 0xB1 0x1C @@ -221,7 +241,7 @@ - ReloopReady.components.rightChannel.rate.inputMSB + ReloopReadyInstance.components.rightChannel.rate.inputMSB 0xB1 0x09 @@ -230,7 +250,7 @@ - ReloopReady.components.rightChannel.rate.inputLSB + ReloopReadyInstance.components.rightChannel.rate.inputLSB 0xB1 0x3F @@ -240,7 +260,7 @@ - ReloopReady.components.rightChannel.load.input + ReloopReadyInstance.components.rightChannel.load.input 0x9E 0x03 @@ -249,7 +269,7 @@ - ReloopReady.components.rightChannel.load.input + ReloopReadyInstance.components.rightChannel.load.input 0x8E 0x03 @@ -259,7 +279,7 @@ - ReloopReady.components.rightChannel.pfl.input + ReloopReadyInstance.components.rightChannel.pfl.input 0x91 0x1B @@ -268,7 +288,7 @@ - ReloopReady.components.rightChannel.pfl.input + ReloopReadyInstance.components.rightChannel.pfl.input 0x81 0x1B @@ -278,7 +298,7 @@ - ReloopReady.components.leftDeck.fxUnit.level.input + ReloopReadyInstance.components.leftDeck.fxUnit.level.input 0xB8 0x00 @@ -288,7 +308,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[0].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[0].input 0x98 0x00 @@ -297,7 +317,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[0].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[0].input 0x88 0x00 @@ -306,7 +326,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[1].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[1].input 0x98 0x01 @@ -315,7 +335,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[1].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[1].input 0x88 0x01 @@ -324,7 +344,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[2].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[2].input 0x98 0x02 @@ -333,7 +353,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[2].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[2].input 0x88 0x02 @@ -342,7 +362,7 @@ - ReloopReady.components.leftDeck.fxUnit.loop.knob.input + ReloopReadyInstance.components.leftDeck.fxUnit.loop.knob.input 0xB4 0x34 @@ -351,7 +371,7 @@ - ReloopReady.components.leftDeck.fxUnit.loop.button.input + ReloopReadyInstance.components.leftDeck.fxUnit.loop.button.input 0x94 0x40 @@ -360,7 +380,7 @@ - ReloopReady.components.leftDeck.fxUnit.loop.button.input + ReloopReadyInstance.components.leftDeck.fxUnit.loop.button.input 0x84 0x40 @@ -370,7 +390,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[0].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[0].input 0x98 0x0A @@ -379,7 +399,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[0].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[0].input 0x88 0x0A @@ -388,7 +408,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[1].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[1].input 0x98 0x0B @@ -397,7 +417,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[1].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[1].input 0x88 0x0B @@ -406,7 +426,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[2].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[2].input 0x98 0x0C @@ -415,7 +435,7 @@ - ReloopReady.components.leftDeck.fxUnit.fxButtons[2].input + ReloopReadyInstance.components.leftDeck.fxUnit.fxButtons[2].input 0x88 0x0C @@ -424,7 +444,7 @@ - ReloopReady.components.leftDeck.fxUnit.loop.knob.input + ReloopReadyInstance.components.leftDeck.fxUnit.loop.knob.input 0xB4 0x03 @@ -433,7 +453,7 @@ - ReloopReady.components.leftDeck.fxUnit.loop.button.input + ReloopReadyInstance.components.leftDeck.fxUnit.loop.button.input 0x94 0x04 @@ -442,7 +462,7 @@ - ReloopReady.components.leftDeck.fxUnit.loop.button.input + ReloopReadyInstance.components.leftDeck.fxUnit.loop.button.input 0x84 0x04 @@ -453,7 +473,7 @@ - ReloopReady.components.rightDeck.fxUnit.level.input + ReloopReadyInstance.components.rightDeck.fxUnit.level.input 0xB9 0x00 @@ -463,7 +483,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[0].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[0].input 0x99 0x00 @@ -472,7 +492,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[0].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[0].input 0x89 0x00 @@ -481,7 +501,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[1].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[1].input 0x99 0x01 @@ -490,7 +510,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[1].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[1].input 0x89 0x01 @@ -499,7 +519,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[2].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[2].input 0x99 0x02 @@ -508,7 +528,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[2].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[2].input 0x89 0x02 @@ -517,7 +537,7 @@ - ReloopReady.components.rightDeck.fxUnit.loop.knob.input + ReloopReadyInstance.components.rightDeck.fxUnit.loop.knob.input 0xB5 0x34 @@ -526,7 +546,7 @@ - ReloopReady.components.rightDeck.fxUnit.loop.button.input + ReloopReadyInstance.components.rightDeck.fxUnit.loop.button.input 0x95 0x40 @@ -535,7 +555,7 @@ - ReloopReady.components.rightDeck.fxUnit.loop.button.input + ReloopReadyInstance.components.rightDeck.fxUnit.loop.button.input 0x85 0x40 @@ -545,7 +565,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[0].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[0].input 0x99 0x0A @@ -554,7 +574,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[0].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[0].input 0x89 0x0A @@ -563,7 +583,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[1].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[1].input 0x99 0x0B @@ -572,7 +592,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[1].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[1].input 0x89 0x0B @@ -581,7 +601,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[2].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[2].input 0x99 0x0C @@ -590,7 +610,7 @@ - ReloopReady.components.rightDeck.fxUnit.fxButtons[2].input + ReloopReadyInstance.components.rightDeck.fxUnit.fxButtons[2].input 0x89 0x0C @@ -599,7 +619,7 @@ - ReloopReady.components.rightDeck.fxUnit.loop.knob.input + ReloopReadyInstance.components.rightDeck.fxUnit.loop.knob.input 0xB5 0x03 @@ -608,7 +628,7 @@ - ReloopReady.components.rightDeck.fxUnit.loop.button.input + ReloopReadyInstance.components.rightDeck.fxUnit.loop.button.input 0x95 0x04 @@ -617,7 +637,7 @@ - ReloopReady.components.rightDeck.fxUnit.loop.button.input + ReloopReadyInstance.components.rightDeck.fxUnit.loop.button.input 0x85 0x04 @@ -627,7 +647,7 @@ - ReloopReady.components.leftDeck.play.input + ReloopReadyInstance.components.leftDeck.play.input 0x90 0x00 @@ -636,7 +656,7 @@ - ReloopReady.components.leftDeck.play.input + ReloopReadyInstance.components.leftDeck.play.input 0x80 0x00 @@ -645,7 +665,7 @@ - ReloopReady.components.leftDeck.play.input + ReloopReadyInstance.components.leftDeck.play.input 0x90 0x10 @@ -654,7 +674,7 @@ - ReloopReady.components.leftDeck.play.input + ReloopReadyInstance.components.leftDeck.play.input 0x80 0x10 @@ -664,7 +684,7 @@ - ReloopReady.components.rightDeck.play.input + ReloopReadyInstance.components.rightDeck.play.input 0x91 0x00 @@ -673,7 +693,7 @@ - ReloopReady.components.rightDeck.play.input + ReloopReadyInstance.components.rightDeck.play.input 0x81 0x00 @@ -682,7 +702,7 @@ - ReloopReady.components.rightDeck.play.input + ReloopReadyInstance.components.rightDeck.play.input 0x91 0x10 @@ -691,7 +711,7 @@ - ReloopReady.components.rightDeck.play.input + ReloopReadyInstance.components.rightDeck.play.input 0x81 0x10 @@ -704,7 +724,7 @@ - ReloopReady.components.leftDeck.cue.input + ReloopReadyInstance.components.leftDeck.cue.input 0x90 0x01 @@ -713,7 +733,7 @@ - ReloopReady.components.leftDeck.cue.input + ReloopReadyInstance.components.leftDeck.cue.input 0x80 0x01 @@ -722,7 +742,7 @@ - ReloopReady.components.leftDeck.cue.input + ReloopReadyInstance.components.leftDeck.cue.input 0x90 0x05 @@ -731,7 +751,7 @@ - ReloopReady.components.leftDeck.cue.input + ReloopReadyInstance.components.leftDeck.cue.input 0x80 0x05 @@ -741,7 +761,7 @@ - ReloopReady.components.rightDeck.cue.input + ReloopReadyInstance.components.rightDeck.cue.input 0x91 0x01 @@ -750,7 +770,7 @@ - ReloopReady.components.rightDeck.cue.input + ReloopReadyInstance.components.rightDeck.cue.input 0x81 0x01 @@ -759,7 +779,7 @@ - ReloopReady.components.rightDeck.cue.input + ReloopReadyInstance.components.rightDeck.cue.input 0x91 0x05 @@ -768,7 +788,7 @@ - ReloopReady.components.rightDeck.cue.input + ReloopReadyInstance.components.rightDeck.cue.input 0x81 0x05 @@ -782,7 +802,7 @@ - ReloopReady.components.leftDeck.sync.input + ReloopReadyInstance.components.leftDeck.sync.input 0x90 0x02 @@ -791,7 +811,7 @@ - ReloopReady.components.leftDeck.sync.input + ReloopReadyInstance.components.leftDeck.sync.input 0x80 0x02 @@ -800,7 +820,7 @@ - ReloopReady.components.leftDeck.sync.input + ReloopReadyInstance.components.leftDeck.sync.input 0x90 0x03 @@ -809,7 +829,7 @@ - ReloopReady.components.leftDeck.sync.input + ReloopReadyInstance.components.leftDeck.sync.input 0x80 0x03 @@ -819,7 +839,7 @@ - ReloopReady.components.rightDeck.sync.input + ReloopReadyInstance.components.rightDeck.sync.input 0x91 0x02 @@ -828,7 +848,7 @@ - ReloopReady.components.rightDeck.sync.input + ReloopReadyInstance.components.rightDeck.sync.input 0x81 0x02 @@ -837,7 +857,7 @@ - ReloopReady.components.rightDeck.sync.input + ReloopReadyInstance.components.rightDeck.sync.input 0x91 0x03 @@ -846,7 +866,7 @@ - ReloopReady.components.rightDeck.sync.input + ReloopReadyInstance.components.rightDeck.sync.input 0x81 0x03 @@ -857,7 +877,7 @@ - ReloopReady.components.leftDeck.vinyl.input + ReloopReadyInstance.components.leftDeck.vinyl.input 0x90 0x0F @@ -866,7 +886,7 @@ - ReloopReady.components.leftDeck.vinyl.input + ReloopReadyInstance.components.leftDeck.vinyl.input 0x80 0x0F @@ -875,7 +895,7 @@ - ReloopReady.components.leftDeck.vinyl.input + ReloopReadyInstance.components.leftDeck.vinyl.input 0x90 0x07 @@ -884,7 +904,7 @@ - ReloopReady.components.leftDeck.vinyl.input + ReloopReadyInstance.components.leftDeck.vinyl.input 0x80 0x07 @@ -894,7 +914,7 @@ - ReloopReady.components.rightDeck.vinyl.input + ReloopReadyInstance.components.rightDeck.vinyl.input 0x91 0x0F @@ -903,7 +923,7 @@ - ReloopReady.components.rightDeck.vinyl.input + ReloopReadyInstance.components.rightDeck.vinyl.input 0x81 0x0F @@ -912,7 +932,7 @@ - ReloopReady.components.rightDeck.vinyl.input + ReloopReadyInstance.components.rightDeck.vinyl.input 0x91 0x07 @@ -921,7 +941,7 @@ - ReloopReady.components.rightDeck.vinyl.input + ReloopReadyInstance.components.rightDeck.vinyl.input 0x81 0x07 @@ -932,7 +952,7 @@ - ReloopReady.components.leftDeck.keylock.input + ReloopReadyInstance.components.leftDeck.keylock.input 0x90 0x0D @@ -941,7 +961,7 @@ - ReloopReady.components.leftDeck.keylock.input + ReloopReadyInstance.components.leftDeck.keylock.input 0x80 0x0D @@ -950,7 +970,7 @@ - ReloopReady.components.leftDeck.keylock.input + ReloopReadyInstance.components.leftDeck.keylock.input 0x90 0x29 @@ -959,7 +979,7 @@ - ReloopReady.components.leftDeck.keylock.input + ReloopReadyInstance.components.leftDeck.keylock.input 0x80 0x29 @@ -969,7 +989,7 @@ - ReloopReady.components.rightDeck.keylock.input + ReloopReadyInstance.components.rightDeck.keylock.input 0x91 0x0D @@ -978,7 +998,7 @@ - ReloopReady.components.rightDeck.keylock.input + ReloopReadyInstance.components.rightDeck.keylock.input 0x81 0x0D @@ -987,7 +1007,7 @@ - ReloopReady.components.rightDeck.keylock.input + ReloopReadyInstance.components.rightDeck.keylock.input 0x91 0x29 @@ -996,7 +1016,7 @@ - ReloopReady.components.rightDeck.keylock.input + ReloopReadyInstance.components.rightDeck.keylock.input 0x81 0x29 @@ -1006,7 +1026,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[0].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[0].input 0x94 0x14 @@ -1015,7 +1035,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[0].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[0].input 0x84 0x14 @@ -1025,7 +1045,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[1].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[1].input 0x94 0x15 @@ -1034,7 +1054,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[1].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[1].input 0x84 0x15 @@ -1044,7 +1064,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[2].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[2].input 0x94 0x16 @@ -1053,7 +1073,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[2].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[2].input 0x84 0x16 @@ -1063,7 +1083,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[3].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[3].input 0x94 0x17 @@ -1072,7 +1092,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[3].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[3].input 0x84 0x17 @@ -1082,7 +1102,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[4].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[4].input 0x94 0x18 @@ -1091,7 +1111,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[4].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[4].input 0x84 0x18 @@ -1101,7 +1121,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[5].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[5].input 0x94 0x19 @@ -1110,7 +1130,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[5].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[5].input 0x84 0x19 @@ -1121,7 +1141,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[6].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[6].input 0x94 0x1A @@ -1130,7 +1150,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[6].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[6].input 0x84 0x1A @@ -1140,7 +1160,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[7].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[7].input 0x94 0x1B @@ -1149,7 +1169,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[7].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[7].input 0x84 0x1B @@ -1160,7 +1180,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[0].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[0].input 0x94 0x1C @@ -1169,7 +1189,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[0].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[0].input 0x84 0x1C @@ -1179,7 +1199,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[1].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[1].input 0x94 0x1D @@ -1188,7 +1208,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[1].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[1].input 0x84 0x1D @@ -1198,7 +1218,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[2].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[2].input 0x94 0x1E @@ -1207,7 +1227,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[2].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[2].input 0x84 0x1E @@ -1217,7 +1237,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[3].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[3].input 0x94 0x1F @@ -1226,7 +1246,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[3].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[3].input 0x84 0x1F @@ -1236,7 +1256,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[4].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[4].input 0x94 0x20 @@ -1245,7 +1265,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[4].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[4].input 0x84 0x20 @@ -1255,7 +1275,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[5].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[5].input 0x94 0x21 @@ -1264,7 +1284,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[5].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[5].input 0x84 0x21 @@ -1275,7 +1295,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[6].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[6].input 0x94 0x22 @@ -1284,7 +1304,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[6].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[6].input 0x84 0x22 @@ -1294,7 +1314,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[7].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[7].input 0x94 0x23 @@ -1303,7 +1323,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.pads[7].input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.pads[7].input 0x84 0x23 @@ -1313,7 +1333,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[0].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[0].input 0x95 0x14 @@ -1322,7 +1342,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[0].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[0].input 0x85 0x14 @@ -1332,7 +1352,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[1].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[1].input 0x95 0x15 @@ -1341,7 +1361,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[1].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[1].input 0x85 0x15 @@ -1351,7 +1371,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[2].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[2].input 0x95 0x16 @@ -1360,7 +1380,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[2].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[2].input 0x85 0x16 @@ -1370,7 +1390,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[3].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[3].input 0x95 0x17 @@ -1379,7 +1399,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[3].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[3].input 0x85 0x17 @@ -1389,7 +1409,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[4].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[4].input 0x95 0x18 @@ -1398,7 +1418,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[4].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[4].input 0x85 0x18 @@ -1408,7 +1428,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[5].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[5].input 0x95 0x19 @@ -1417,7 +1437,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[5].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[5].input 0x85 0x19 @@ -1428,7 +1448,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[6].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[6].input 0x95 0x1A @@ -1437,7 +1457,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[6].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[6].input 0x85 0x1A @@ -1447,7 +1467,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[7].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[7].input 0x95 0x1B @@ -1456,7 +1476,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[7].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[7].input 0x85 0x1B @@ -1467,7 +1487,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[0].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[0].input 0x95 0x1C @@ -1476,7 +1496,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[0].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[0].input 0x85 0x1C @@ -1486,7 +1506,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[1].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[1].input 0x95 0x1D @@ -1495,7 +1515,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[1].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[1].input 0x85 0x1D @@ -1505,7 +1525,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[2].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[2].input 0x95 0x1E @@ -1514,7 +1534,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[2].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[2].input 0x85 0x1E @@ -1524,7 +1544,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[3].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[3].input 0x95 0x1F @@ -1533,7 +1553,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[3].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[3].input 0x85 0x1F @@ -1543,7 +1563,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[4].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[4].input 0x95 0x20 @@ -1552,7 +1572,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[4].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[4].input 0x85 0x20 @@ -1562,7 +1582,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[5].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[5].input 0x95 0x21 @@ -1571,7 +1591,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[5].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[5].input 0x85 0x21 @@ -1582,7 +1602,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[6].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[6].input 0x95 0x22 @@ -1591,7 +1611,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[6].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[6].input 0x85 0x22 @@ -1601,7 +1621,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[7].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[7].input 0x95 0x23 @@ -1610,7 +1630,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.pads[7].input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.pads[7].input 0x85 0x23 @@ -1620,7 +1640,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.parameterRight.input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.parameterRight.input 0x95 0x29 @@ -1629,7 +1649,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.parameterRight.input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.parameterRight.input 0x85 0x29 @@ -1638,7 +1658,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.parameterLeft.input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.parameterLeft.input 0x95 0x28 @@ -1647,7 +1667,7 @@ - ReloopReady.components.rightDeck.padUnit.currentLayer.parameterLeft.input + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.parameterLeft.input 0x85 0x28 @@ -1657,7 +1677,7 @@ - ReloopReady.components.rightDeck.padUnit.modeButton.input + ReloopReadyInstance.components.rightDeck.padUnit.modeButton.input 0x91 0x20 @@ -1666,7 +1686,7 @@ - ReloopReady.components.rightDeck.padUnit.modeButton.input + ReloopReadyInstance.components.rightDeck.padUnit.modeButton.input 0x81 0x20 @@ -1675,7 +1695,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[0].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[0].input 0x95 0x00 @@ -1684,7 +1704,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[0].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[0].input 0x85 0x00 @@ -1693,7 +1713,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[1].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[1].input 0x95 0x05 @@ -1702,7 +1722,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[1].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[1].input 0x85 0x05 @@ -1711,7 +1731,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[2].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[2].input 0x95 0x0E @@ -1720,7 +1740,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[2].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[2].input 0x85 0x0E @@ -1729,7 +1749,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[3].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[3].input 0x95 0x0B @@ -1738,7 +1758,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[3].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[3].input 0x85 0x0B @@ -1747,7 +1767,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[4].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[4].input 0x95 0x0F @@ -1756,7 +1776,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[4].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[4].input 0x85 0x0F @@ -1765,7 +1785,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[5].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[5].input 0x95 0x09 @@ -1774,7 +1794,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[5].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[5].input 0x85 0x09 @@ -1783,7 +1803,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[6].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[6].input 0x95 0x08 @@ -1792,7 +1812,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[6].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[6].input 0x85 0x08 @@ -1801,7 +1821,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[7].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[7].input 0x95 0x12 @@ -1810,7 +1830,7 @@ - ReloopReady.components.rightDeck.padUnit.padModeSelectors[7].input + ReloopReadyInstance.components.rightDeck.padUnit.padModeSelectors[7].input 0x85 0x12 @@ -1821,7 +1841,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.parameterRight.input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.parameterRight.input 0x94 0x29 @@ -1830,7 +1850,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.parameterRight.input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.parameterRight.input 0x84 0x29 @@ -1839,7 +1859,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.parameterLeft.input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.parameterLeft.input 0x94 0x28 @@ -1848,7 +1868,7 @@ - ReloopReady.components.leftDeck.padUnit.currentLayer.parameterLeft.input + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.parameterLeft.input 0x84 0x28 @@ -1857,7 +1877,7 @@ - ReloopReady.components.leftDeck.padUnit.modeButton.input + ReloopReadyInstance.components.leftDeck.padUnit.modeButton.input 0x90 0x20 @@ -1866,7 +1886,7 @@ - ReloopReady.components.leftDeck.padUnit.modeButton.input + ReloopReadyInstance.components.leftDeck.padUnit.modeButton.input 0x80 0x20 @@ -1875,7 +1895,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[0].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[0].input 0x94 0x00 @@ -1884,7 +1904,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[0].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[0].input 0x84 0x00 @@ -1893,7 +1913,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[1].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[1].input 0x94 0x05 @@ -1902,7 +1922,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[1].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[1].input 0x84 0x05 @@ -1911,7 +1931,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[2].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[2].input 0x94 0x0E @@ -1920,7 +1940,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[2].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[2].input 0x84 0x0E @@ -1929,7 +1949,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[3].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[3].input 0x94 0x0B @@ -1938,7 +1958,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[3].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[3].input 0x84 0x0B @@ -1947,7 +1967,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[4].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[4].input 0x94 0x0F @@ -1956,7 +1976,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[4].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[4].input 0x84 0x0F @@ -1965,7 +1985,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[5].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[5].input 0x94 0x09 @@ -1974,7 +1994,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[5].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[5].input 0x84 0x09 @@ -1983,7 +2003,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[6].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[6].input 0x94 0x08 @@ -1992,7 +2012,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[6].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[6].input 0x84 0x08 @@ -2001,7 +2021,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[7].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[7].input 0x94 0x12 @@ -2010,7 +2030,7 @@ - ReloopReady.components.leftDeck.padUnit.padModeSelectors[7].input + ReloopReadyInstance.components.leftDeck.padUnit.padModeSelectors[7].input 0x84 0x12 @@ -2020,7 +2040,7 @@ - ReloopReady.shift.input + ReloopReadyInstance.shift.input 0x9E 0x00 @@ -2029,7 +2049,7 @@ - ReloopReady.shift.input + ReloopReadyInstance.shift.input 0x8E 0x00 @@ -2039,7 +2059,7 @@ - ReloopReady.components.crossfader.input + ReloopReadyInstance.components.crossfader.input 0xBE 0x08 @@ -2049,7 +2069,7 @@ - ReloopReady.components.cueVol.input + ReloopReadyInstance.components.cueVol.input 0xBE 0x0C @@ -2058,7 +2078,7 @@ - ReloopReady.components.cueMix.input + ReloopReadyInstance.components.cueMix.input 0xBE 0x0D @@ -2067,7 +2087,7 @@ - ReloopReady.components.masterVol.input + ReloopReadyInstance.components.masterVol.input 0xBE 0x0A @@ -2077,7 +2097,7 @@ - ReloopReady.components.browse.button.input + ReloopReadyInstance.components.browse.button.input 0x9E 0x06 @@ -2086,7 +2106,7 @@ - ReloopReady.components.browse.button.input + ReloopReadyInstance.components.browse.button.input 0x8E 0x06 @@ -2095,7 +2115,7 @@ - ReloopReady.components.browse.button.input + ReloopReadyInstance.components.browse.button.input 0x9E 0x7B @@ -2104,7 +2124,7 @@ - ReloopReady.components.browse.button.input + ReloopReadyInstance.components.browse.button.input 0x8E 0x7B @@ -2114,7 +2134,7 @@ - ReloopReady.components.browse.knob.input + ReloopReadyInstance.components.browse.knob.input 0xBE 0x00 @@ -2123,7 +2143,7 @@ - ReloopReady.components.browse.knob.input + ReloopReadyInstance.components.browse.knob.input 0xBE 0x78 @@ -2132,7 +2152,7 @@ - + 0xF0 diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index c0c06035101..666b593f8df 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -1,70 +1,205 @@ -// eslint-ignore-next-line no-var -var ReloopReady = {}; -/// The controller only offers Low, High and Gain Knobs, -/// settings this to true will remap them to control Low, Mid, High instead. -ReloopReady.threeBandEQ = false; +// eslint-disable-next-line no-var, no-unused-vars +var ReloopReadyInstance = { + init: function() { + ReloopReady.init(); -ReloopReady.backlightButtons = true; - -/// scratch parameter, change to your liking -ReloopReady.scratchParams = { - alpha: 1/8, - beta: (1/8)/32, + } }; -/// On my hardware unit, the tempo faders have such a cheap build-quality -/// that the notches in the center are not actually in the middle -/// If you find your notches are offset too, do this: -/// Load this mapping in mixxx using `--developer` mode. -/// Then move the tempo fader into the notch at the position where -/// the tempo should then be the true tempo (+/- 0). Open the developer tools -/// and read the `rate` value of the channel. If its already 0.5 you should be -/// good, if its not that, copy the value here and the fader will be adjusted -/// for you. -ReloopReady.tempoFaderMiddles = [0.5, 0.5]; - - -/** - * Misc. midi notes - * - * Note 0x31 firmware fader-start feature. Note 0x7F if volume fader is non-zero - */ - - -/** - * Color: - * The Reloop ready uses the entire 7-bit midi range for color. - * It encodes it in a simple to understand (but inefficient in regards to - * colors perceived by us) scheme: - * It uses the lower 6-bit for RGB colors (each channel having the unbelievable - * resolution of 2-bit), while the 7th-bit is used to switch between two - * brightness/intensity modes (with the 7th-bit set, meaning high brightness). - * - * |7654321| - * +-------+ - * |IRRGGBB| - * Example: the fullest, brightest red would have I set and the red channel on - * maximum: 0b1110000 - * - * Using that knowledge, we can extrapolate a 24-bit color from any 7-bit color: - */ - -ReloopReady.midiToFullColor = color => { - - const b = (color & 0b00000011) << 6; - const g = (color & 0b00001100) << 4; - const r = (color & 0b00110000) << 2; - const i = (color & 0b01000000) >> 6; - - if (i === 0) { - // half the RGB intensity - r >> 0b1; - g >> 0b1; - b >> 0b1; +class ReloopReady { + static init() { + ReloopReadyInstance = new ReloopReady(); + } + constructor() { + + /// The controller only offers Low, High and Gain Knobs, + /// settings this to true will remap them to control Low, Mid, High instead. + this.threeBandEQ = false; + + this.backlightButtons = true; + + /// On my hardware unit, the tempo faders have such a cheap build-quality + /// that the notches in the center are not actually in the middle + /// If you find your notches are offset too, do this: + /// Load this mapping in mixxx using `--developer` mode. + /// Then move the tempo fader into the notch at the position where + /// the tempo should then be the true tempo (+/- 0). Open the developer tools + /// and read the `rate` value of the channel. If its already 0.5 you should be + /// good, if its not that, copy the value here and the fader will be adjusted + /// for you. + this.tempoFaderMiddles = [0.5, 0.5]; + + + components.Button.prototype.off = this.backlightButtons ? this.constructor.singleColorLED.dim : this.constructor.singleColorLED.off; + components.Button.prototype.on = this.constructor.singleColorLED.lit; + + + /** + * Misc. midi notes + * + * Note 0x31 firmware fader-start feature. Note 0x7F if volume fader is non-zero + */ + + this.components = new components.ComponentContainer(); + + this.components.leftDeck = new ReloopReady.Deck(0); + this.components.rightDeck = new ReloopReady.Deck(1); + this.components.leftChannel = new ReloopReady.Channel(0, this); + this.components.rightChannel = new ReloopReady.Channel(1, this); + + this.components.crossfader = new components.Pot({ + midi: [0xBE, 0x08], + group: "[Master]", + inKey: "crossfader" + }); + + this.components.masterVol = new components.Pot({ + midi: [0xBE, 0x0A], + group: "[Master]", + inKey: "gain" + }); + + this.components.cueMix = new components.Pot({ + midi: [0xBE, 0x0D], + group: "[Master]", + inKey: "headMix" + }); + + this.components.cueVol = new components.Pot({ + midi: [0xBE, 0x0C], + group: "[Master]", + inKey: "headGain" + }); + + this.components.browse = new components.ComponentContainer({ + button: new components.Button({ + midi: [0x9E, 0x06], + unshift: function() { + this.group = "[Library]"; + this.inKey = "GoToItem"; + this.type = components.Button.prototype.types.push; + }, + shift: function() { + this.group = "[Master]"; + this.inKey = "maximize_library"; + this.type = components.Button.prototype.types.toggle; + } + }), + knob: new components.Encoder({ + midi: [0xBE, 0x00], + group: "[Library]", + unshift: function() { + this.inKey = "MoveVertical"; + }, + shift: function() { + this.inKey = "MoveFocus"; + } + }) + }); + + const shiftableComponents = this.components; + + this.shift = new components.Button({ + midi: [0x9E, 0x00], + input: function(channel, control, value, status, _group) { + shiftableComponents.forEachComponent(comp => comp.disconnect()); + if (this.isPress(channel, control, value, status)) { + shiftableComponents.shift(); + } else { + shiftableComponents.unshift(); + } + shiftableComponents.forEachComponent(comp => { + comp.connect(); + comp.trigger(); + }); + }, + }); + + // request controls status per standard serato sysex + midi.sendSysexMsg([0xF0, 0x00, 0x20, 0x7F, 0x00, 0xF7]); + // request firmware version + midi.sendSysexMsg(this.constructor.requestFirmwareVersionSysex.concat([0xF7])); + } + + /** + * Handle incoming sysex message + * @param {Array} data sysex data received from the controller + */ + incomingData(data) { + const arrayStartsWith = (arr, pattern) => pattern.every((elem, i) => elem === arr[i]); + if (arrayStartsWith(data, this.constructor.requestFirmwareVersionSysex)) { + const firmwareVersion = data.slice(this.constructor.requestFirmwareVersionSysex.length, data.length - 1); + // no idea if that's actually the format used for the firmware version, + // its just 4 bytes (7-bit cuz midi) for me. + console.log(`firmware version: ${firmwareVersion.join(".")}`); + return; + } + console.warn(`unregonized incoming sysex data: ${data}`); + }; + + shutdown() { + this.components.shutdown(); + } + + /** + * Color: + * The Reloop ready uses the entire 7-bit midi range for color. + * It encodes it in a simple to understand (but inefficient in regards to + * colors perceived by us) scheme: + * It uses the lower 6-bit for RGB colors (each channel having the unbelievable + * resolution of 2-bit), while the 7th-bit is used to switch between two + * brightness/intensity modes (with the 7th-bit set, meaning high brightness). + * + * |7654321| + * +-------+ + * |IRRGGBB| + * Example: the fullest, brightest red would have I set and the red channel on + * maximum: 0b1110000 + * + * Using that knowledge, we can extrapolate a 24-bit color from any 7-bit color: + * @param {number} color a midi value representing the color + * @returns {number} 24-bit (8-bit per channel) upscaled `color` + */ + static midiToFullColor(color) { + const b = (color & 0b00000011) << 6; + const g = (color & 0b00001100) << 4; + const r = (color & 0b00110000) << 2; + const i = (color & 0b01000000) >> 6; + + if (i === 0) { + // half the RGB intensity + r >> 0b1; + g >> 0b1; + b >> 0b1; + } + return (r << 16) | (g << 8) | b; + } + static dimColor(color) { + return color & 0b00111111; } - return (r << 16) | (g << 8) | b; -}; + + + /** + * creates an this.isPress guarded input handler, assumes to be called by components.Button or subclasses thereof + * @param {(value: number) => void} handler callback that is called on ButtonDown + * @returns {(channel: number, control: number, value: number, status: number, group: string) => void} XML input handler + */ + static makeButtonDownInputHandler(handler) { + return function(channel, control, value, status, _group) { + this.isPressed = this.isPress(channel, control, value, status); + this.trigger(); + if (this.isPressed) { + handler.call(this, value); + } + }; + } + static makeIsPressedTrigger() { + return function() { + this.output(this.isPressed); + }; + } +} ReloopReady.padColorPalette = { Off: 0x000000, @@ -79,14 +214,15 @@ ReloopReady.padColorPalette = { ReloopReady.requestFirmwareVersionSysex = [0xF0, 0x00, 0x20, 0x7F, 0x19]; -// dim color by stripping the "intensity bit" -ReloopReady.dimColor = color => color & 0b00111111; +ReloopReady.singleColorLED = { + off: 0x00, + dim: 0x01, + lit: 0x7F, // anything between 0x02 and 0x7F is fully lit +}; -ReloopReady.noopInputHandler = function(_channel, _control, _value, _status, _group) {}; +ReloopReady.noopInputHandler = (_channel, _control, _value, _status, _group) => {}; -// IIFE for not leaking "private" variables (() => { - // rewrite the following without lodash const fullColorToMidiColorMap = _(_.range(0x00, 0x80)) .keyBy() @@ -115,239 +251,121 @@ components.Encoder.prototype.inValueScale = function(value) { return value < 0x40 ? value : value - 0x80; }; -ReloopReady.singleColorLED = { - off: 0x00, - dim: 0x01, - lit: 0x7F, // anything between 0x02 and 0x7F is fully lit -}; +ReloopReady.Channel = class extends components.ComponentContainer { + constructor(deckIdx, parent) { + super(); + const channel = deckIdx + 1; + const group = `[Channel${channel}]`; + const eqGroup = `[EqualizerRack1_${group}_Effect1]`; + + if (parent.threeBandEQ) { + this.knob1 = new components.Pot({ + midi: [0xB0 + deckIdx, 0x16], + group: eqGroup, + inKey: "parameter3" + }); + this.knob2 = new components.Pot({ + midi: [0xB0 + deckIdx, 0x17], + group: eqGroup, + inKey: "parameter2" + }); + } else { + this.knob1 = new components.Pot({ + midi: [0xB0 + deckIdx, 0x16], + inKey: "pregain" + }); -// make Buttons backlight be default based on user-setting -components.Button.prototype.off = ReloopReady.backlightButtons ? ReloopReady.singleColorLED.dim : ReloopReady.singleColorLED.off; -components.Button.prototype.on = ReloopReady.singleColorLED.lit; - -/** - * creates an this.isPress guarded input handler, assumes to be called by components.Button or subclasses thereof - * @param {(value: number) => void} func callback that is called on ButtonDown - * @returns {(channel: number, control: number, value: number, status: number, group: string) => void} XML input handler - */ -ReloopReady.makeButtonDownInputHandler = function(func) { - return function(channel, control, value, status, _group) { - const isPress = this.isPress(channel, control, value, status); - this.output(isPress); - if (!isPress) { - return; + this.knob2 = new components.Pot({ + midi: [0xB0 + deckIdx, 0x17], + group: eqGroup, + inKey: "parameter3" + }); } - func.call(this, value); - }; -}; -/** - * Handle incoming sysex message - * @param {Array} data sysex data received from the controller - */ -ReloopReady.incomingData = data => { - const arrayStartsWith = (arr, pattern) => pattern.every((elem, i) => elem === arr[i]); - if (arrayStartsWith(data, ReloopReady.requestFirmwareVersionSysex)) { - const firmwareVersion = data.slice(ReloopReady.requestFirmwareVersionSysex.length, data.length - 1); - // no idea if that's actually the format used for the firmware version, - // its just 4 bytes (7-bit cuz midi) for me. - console.log(`firmware version: ${firmwareVersion.join(".")}`); - return; - } - console.warn(`unregonized incoming sysex data: ${data}`); -}; - -ReloopReady.init = function() { - - this.components = new components.ComponentContainer(); + this.knob3 = new components.Pot({ + midi: [0xB0 + deckIdx, 0x19], + group: eqGroup, + inKey: "parameter1" + }); - this.components.leftDeck = new ReloopReady.Deck(0); - this.components.rightDeck = new ReloopReady.Deck(1); - this.components.leftChannel = new ReloopReady.Channel(0); - this.components.rightChannel = new ReloopReady.Channel(1); - this.components.crossfader = new components.Pot({ - midi: [0xBE, 0x08], - group: "[Master]", - inKey: "crossfader" - }); + this.filter = new components.Pot({ + midi: [0xB0 + deckIdx, 0x1A], + group: `[QuickEffectRack1_${group}]`, + inKey: "super1" + }); - this.components.masterVol = new components.Pot({ - midi: [0xBE, 0x0A], - group: "[Master]", - inKey: "gain" - }); + this.rate = new components.Pot({ + midi: [0xB0 + deckIdx, 0x09], // MSB control: 0x3F + inKey: "rate", + // tempo fader response: top ("-") @ 0, bottom ("+") @ 1023 + max: 1023, + invert: true, + // see explanation of ReloopReady.tempoFaderMiddles for info + inValueScale: function(value) { + const middle = (this.max + 1) * parent.tempoFaderMiddles[deckIdx]; + return script.absoluteNonLinInverse(value, 0, middle, this.max, 0, 1); + } + }); - this.components.cueMix = new components.Pot({ - midi: [0xBE, 0x0D], - group: "[Master]", - inKey: "headMix" - }); + this.volume = new components.Pot({ + midi: [0xB0 + deckIdx, 0x1C], + inKey: "volume", + // whenever we shift, the component triggers softtakeover. + // this is usually not a problem on higher-end hardware, + // but on this controller, the fader is so-low resolution + // that a quick movement can lead to such a large difference, + // that it really does seem like the fader has been moved + // while shifting. So softtakeover is engaged, even though + // it shouldn't which makes the volume fader unresponsive + // in that edge case (this is perceived to happen randomly + // to the untrained user and definitely not desirable). + // So opt out of any softTakeover-trickery. + softTakeover: false, + }); - this.components.cueVol = new components.Pot({ - midi: [0xBE, 0x0C], - group: "[Master]", - inKey: "headGain" - }); + this.pfl = new components.Button({ + midi: [0x90 + deckIdx, 0x1B], + shiftOffset: -0xD, + key: "pfl", + type: components.Button.prototype.types.toggle, + }); - this.components.browse = new components.ComponentContainer({ - button: new components.Button({ - midi: [0x9E, 0x06], - unshift: function() { - this.group = "[Library]"; - this.inKey = "GoToItem"; - this.type = components.Button.prototype.types.push; - }, + this.load = new components.Button({ + midi: [0x9E, 0x02 + deckIdx], + shiftOffset: 0x0D, shift: function() { - this.group = "[Master]"; - this.inKey = "maximize_library"; - this.type = components.Button.prototype.types.toggle; - } - }), - knob: new components.Encoder({ - midi: [0xBE, 0x00], - group: "[Library]", - unshift: function() { - this.inKey = "MoveVertical"; + this.inKey = "eject"; + this.outKey = this.inKey; }, - shift: function() { - this.inKey = "MoveFocus"; + unshift: function() { + this.inKey = "LoadSelectedTrack"; + this.outKey = this.inKey; } - }) - }); + }); - const shiftableComponents = this.components; - this.shift = new components.Button({ - midi: [0x9E, 0x00], - input: function(channel, control, value, status, _group) { - shiftableComponents.forEachComponent(comp => comp.disconnect()); - if (this.isPress(channel, control, value, status)) { - shiftableComponents.shift(); - } else { - shiftableComponents.unshift(); + this.reconnectComponents(c => { + if (c.group === undefined) { + c.group = group; } - shiftableComponents.forEachComponent(comp => { - comp.connect(); - comp.trigger(); - }); - }, - }); - - // request controls status per standard serato sysex - midi.sendSysexMsg([0xF0, 0x00, 0x20, 0x7F, 0x00, 0xF7]); - // request firmware version - midi.sendSysexMsg(ReloopReady.requestFirmwareVersionSysex.concat([0xF7])); -}; - -ReloopReady.shutdown = () => { - ReloopReady.components.shutdown(); -}; - - -ReloopReady.Channel = function(index) { - const channel = index + 1; - const group = `[Channel${channel}]`; - const eqGroup = `[EqualizerRack1_${group}_Effect1]`; - - if (ReloopReady.threeBandEQ) { - this.knob1 = new components.Pot({ - midi: [0xB0 + index, 0x16], - group: eqGroup, - inKey: "parameter3" - }); - this.knob2 = new components.Pot({ - midi: [0xB0 + index, 0x17], - group: eqGroup, - inKey: "parameter2" - }); - } else { - this.knob1 = new components.Pot({ - midi: [0xB0 + index, 0x16], - inKey: "pregain" - }); - - this.knob2 = new components.Pot({ - midi: [0xB0 + index, 0x17], - group: eqGroup, - inKey: "parameter3" }); } - - this.knob3 = new components.Pot({ - midi: [0xB0 + index, 0x19], - group: eqGroup, - inKey: "parameter1" - }); - - - this.filter = new components.Pot({ - midi: [0xB0 + index, 0x1A], - group: `[QuickEffectRack1_${group}]`, - inKey: "super1" - }); - - this.rate = new components.Pot({ - midi: [0xB0 + index, 0x09], // MSB control: 0x3F - inKey: "rate", - // tempo fader response: top ("-") @ 0, bottom ("+") @ 1023 - max: 1023, - invert: true, - // see explanation of ReloopReady.tempoFaderMiddles for info - inValueScale: function(value) { - const middle = (this.max + 1) * ReloopReady.tempoFaderMiddles[index]; - return script.absoluteNonLinInverse(value, 0, middle, this.max, 0, 1); - } - }); - - // TODO this seems to have soft-takeover enabled sometimes?! - this.volume = new components.Pot({ - midi: [0xB0 + index, 0x1C], - inKey: "volume", - }); - - this.pfl = new components.Button({ - midi: [0x90 + index, 0x1B], - shiftOffset: -0xD, - key: "pfl", - type: components.Button.prototype.types.toggle, - }); - - this.load = new components.Button({ - midi: [0x9E, 0x02 + index], - shiftOffset: 0x0D, - shift: function() { - this.inKey = "eject"; - this.outKey = this.inKey; - }, - unshift: function() { - this.inKey = "LoadSelectedTrack"; - this.outKey = this.inKey; - } - }); - - - this.reconnectComponents(c => { - if (c.group === undefined) { - c.group = group; - } - }); }; +ReloopReady.FxUnit = class extends components.ComponentContainer { + constructor(deckIdx) { + super(); -ReloopReady.Channel.prototype = new components.ComponentContainer(); + const midiOn = 0x98 + deckIdx; + const channel = deckIdx + 1; -ReloopReady.FxUnit = function(index) { - - const midiOn = 0x98 + index; - const channel = index + 1; + this.level = new components.Pot({ + midi: [0xB8 + deckIdx, 0x00], + group: `[EffectRack1_EffectUnit${channel}]`, + inKey: "super1", + }); - this.level = new components.Pot({ - midi: [0xB8 + index, 0x00], - group: `[EffectRack1_EffectUnit${channel}]`, - inKey: "super1", - }); - this.fxButtons = Array.from([0x00, 0x01, 0x02, 0x03], control => - new components.Button({ + this.fxButtons = [0x00, 0x01, 0x02, 0x03].map(control => new components.Button({ midi: [midiOn, control], shiftOffset: 0xA, group: `[EffectRack1_EffectUnit${channel}_Effect${control + 1}]`, @@ -361,112 +379,97 @@ ReloopReady.FxUnit = function(index) { this.outKey = "loaded"; this.type = components.Button.prototype.types.push; } - }) - ); - - this.loop = { - knob: new components.Encoder({ - midi: [0xB4 + index, 0x03], - group: `[Channel${channel}]`, - input: function(_channel, _control, value, _status, _group) { - engine.setValue(this.group, value > 0x40 ? "loop_halve" : "loop_double", 1); - } - }), - button: new components.Button({ - midi: [0x94 + index, 0x40], // shifted: [0x98 + index, 0x04] - group: `[Channel${channel}]`, - input: function(channel, control, value, status, _group) { - if (this.isPress(channel, control, value, status)) { - if (this.shifted) { - // non standard behavior - script.bpm.tapButton(index); - } else { - // TODO: For 2.4 using plain beatloop_activate - // is sufficient, see #4328 - if (engine.getValue(this.group, "loop_enabled")) { - script.triggerControl(this.group, "reloop_toggle"); + })); + + this.loop = { + knob: new components.Encoder({ + midi: [0xB4 + deckIdx, 0x03], + group: `[Channel${channel}]`, + input: function(_channel, _control, value, _status, _group) { + engine.setValue(this.group, value > 0x40 ? "loop_halve" : "loop_double", 1); + } + }), + button: new components.Button({ + midi: [0x94 + deckIdx, 0x40], // shifted: [0x98 + deckIdx, 0x04] + group: `[Channel${channel}]`, + input: function(channel, control, value, status, _group) { + if (this.isPress(channel, control, value, status)) { + if (this.shifted) { + // non standard behavior + script.bpm.tapButton(deckIdx); } else { script.triggerControl(this.group, "beatloop_activate"); } } - } - }, - }) - }; + }, + }) + }; + } }; - -ReloopReady.FxUnit.prototype = new components.ComponentContainer(); - - -ReloopReady.PadMode = function(obj) { - components.ComponentContainer.call(this, obj); - // interpret this an interface definition - // we fill this array to avoid the issues with iteration methods and sparse arrays - this.pads = Array.from(Array(8), (_, i) => 0x14 + i); - this.parameterLeft = new components.Button(); - this.parameterRight = new components.Button(); - this.shutdown = () => { +ReloopReady.PadMode = class extends components.ComponentContainer { + constructor(deckIdx) { + super(); + // interpret this an interface definition + // we fill this array to avoid the issues with iteration methods and sparse arrays + this.pads = Array.from(Array(8), (_, i) => 0x14 + i); + this.parameterLeft = new components.Button({ + midi: [0x94 + deckIdx, 0x28], //shifted control: 0x2B + shiftOffset: 0x2, + off: ReloopReady.singleColorLED.off, + trigger: function() { + this.send(this.off); + } + }); + this.parameterRight = new components.Button({ + midi: [0x94 + deckIdx, 0x29], //shifted control: 0x2B + shiftOffset: 0x2, + off: ReloopReady.singleColorLED.off, + trigger: function() { + this.send(this.off); + } + }); + } + shutdown() { // we want the pads to be completely off when shutdown, not dimmed - this.pads.forEach(pad => pad.send(0x00)); + this.pads.forEach(pad => pad.send(ReloopReady.singleColorLED.off,)); this.parameterLeft.shutdown(); this.parameterRight.shutdown(); - }; + } }; - -ReloopReady.PadMode.prototype = Object.create(components.ComponentContainer.prototype); - -ReloopReady.HotcuePadMode = function(index) { - ReloopReady.PadMode.call(this); - // can't use map on sparse arrays, so we use.from - this.pads = this.pads.map((control, i) => - new components.HotcueButton({ - midi: [0x94 + index, control], - group: `[Channel${index + 1}]`, +ReloopReady.HotcuePadMode = class extends ReloopReady.PadMode { + constructor(deckIdx) { + super(deckIdx); + this.pads = this.pads.map((control, i) => new components.HotcueButton({ + midi: [0x94 + deckIdx, control], + group: `[Channel${deckIdx + 1}]`, shiftOffset: 0x8, off: 0x00, number: i + 1, colorMapper: ReloopReady.padColorMapper, outConnect: false, - }) - ); + })); + } }; -ReloopReady.HotcuePadMode.prototype = Object.create(ReloopReady.PadMode.prototype); - -ReloopReady.AutoLoopPadMode = function(index) { - ReloopReady.PadMode.call(this); - - this.currentLoopSizeExp = -2; - const theContainer = this; - - const clampLoopSizeExp = loopSizeExp => _.clamp(loopSizeExp, -5, -2); - - this.setLoopSizes = loopSizeExp => { - _(_.range(loopSizeExp, loopSizeExp + 8)) - .map(function(exp) { return Math.pow(2, exp); }) +ReloopReady.AbstractLoopPadMode = class extends ReloopReady.PadMode { + clampLoopSizeExp(loopSizeExp) { + return _.clamp(loopSizeExp, -5, 2); + } + setLoopSizeProperties([_size, _pad]) { + throw new TypeError("AbstractLoopPadMode.setLoopSizeProperties not overwritten!"); + } + setLoopSizes(loopSizeExp) { + _(_.range(loopSizeExp, loopSizeExp + this.pads.length)) + .map(exp => Math.pow(2, exp)) .zip(this.pads) - .forEach(([size, pad]) => { - pad.inKey = `beatloop_${size}_toggle`; - pad.outKey = `beatloop_${size}_enabled`; - }); - }; - - for (let i = 0; i < this.pads.length; i++) { - this.pads[i] = new components.Button({ - midi: [0x94 + index, 0x14 + i], - group: `[Channel${index + 1}]`, - shiftOffset: 0x8, - off: ReloopReady.padColorPalette.Red.dim, - on: ReloopReady.padColorPalette.Red.lit, - outConnect: false, - }); + .forEach(this.setLoopSizeProperties); } - - const makeParameterInputHandler = loopSizeChangeAmount => { + makeParameterInputHandler(loopSizeChangeAmount) { + const theContainer = this; return function(channel, control, value, status, _group) { const pressed = this.isPress(channel, control, value, status); if (pressed) { - const newLoopSize = clampLoopSizeExp(theContainer.currentLoopSizeExp + loopSizeChangeAmount); + const newLoopSize = theContainer.clampLoopSizeExp(theContainer.currentLoopSizeExp + loopSizeChangeAmount); if (newLoopSize !== theContainer.currentLoopSizeExp) { theContainer.currentLoopSizeExp = newLoopSize; theContainer.setLoopSizes(newLoopSize); @@ -475,485 +478,467 @@ ReloopReady.AutoLoopPadMode = function(index) { } this.output(pressed); }; - }; - - this.parameterLeft = new components.Button({ - midi: [0x94 + index, 0x28], //shifted control: 0x2A - shiftOffset: 0x2, - input: makeParameterInputHandler(-1), - }); - this.parameterRight = new components.Button({ - midi: [0x94 + index, 0x29], //shifted control: 0x2B - shiftOffset: 0x2, - input: makeParameterInputHandler(1), - }); - - this.setLoopSizes(this.currentLoopSizeExp); + } + constructor(deckIdx) { + super(deckIdx); + // https://stackoverflow.com/a/30560792 + if (new.target === ReloopReady.AbstractLoopPadMode) { + throw new TypeError("Cannot construct AbstractLoopPadMode instances directly"); + } + } }; +ReloopReady.AutoLoopPadMode = class extends ReloopReady.AbstractLoopPadMode { -ReloopReady.AutoLoopPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); + setLoopSizeProperties([size, pad]) { + pad.inKey = `beatloop_${size}_toggle`; + pad.outKey = `beatloop_${size}_enabled`; + } -ReloopReady.ManualLoopPadMode = function(index) { - ReloopReady.PadMode.call(this); + constructor(deckIdx) { + super(deckIdx); - let loopMoveBeats = 4; - const group = `[Channel${index + 1}]`; + this.currentLoopSizeExp = -2; - this.pads[0] = new components.Button({ - key: "loop_in", - on: ReloopReady.padColorPalette.Blue.lit, - off: ReloopReady.padColorPalette.Blue.dim, - }); - this.pads[1] = new components.Button({ - key: "loop_out", - on: ReloopReady.padColorPalette.Blue.lit, - off: ReloopReady.padColorPalette.Blue.dim, - }); - this.pads[2] = new components.Button({ - inKey: "reloop_toggle", - outKey: "loop_enabled", - on: ReloopReady.padColorPalette.Red.lit, - off: ReloopReady.padColorPalette.Red.dim, - }); - this.pads[3] = new components.Button({ - key: "loop_in_goto", - on: ReloopReady.padColorPalette.Red.lit, - off: ReloopReady.padColorPalette.Red.dim, - }); - this.pads[4] = new components.Button({ - key: "loop_halve", - on: ReloopReady.padColorPalette.Green.lit, - off: ReloopReady.padColorPalette.Green.dim, - }); - this.pads[5] = new components.Button({ - key: "loop_double", - on: ReloopReady.padColorPalette.Green.lit, - off: ReloopReady.padColorPalette.Green.dim, - }); - this.pads[6] = new components.Button({ - inKey: "loop_move", - on: ReloopReady.padColorPalette.Cyan.lit, - off: ReloopReady.padColorPalette.Cyan.dim, - unshift: function() { - this.input = ReloopReady.makeButtonDownInputHandler(function() { - this.inSetValue(-loopMoveBeats); - }); - }, - shift: function() { - this.input = ReloopReady.makeButtonDownInputHandler(function() { - this.inSetValue(-engine.getValue(this.group, "loop_size")); - }); - }, - trigger: function() { - this.output(); - }, - }); - this.pads[7] = new components.Button({ - inKey: "loop_move", - on: ReloopReady.padColorPalette.Cyan.lit, - off: ReloopReady.padColorPalette.Cyan.dim, - unshift: function() { - this.input = ReloopReady.makeButtonDownInputHandler(function() { - this.inSetValue(loopMoveBeats); - }); - }, - shift: function() { - this.input = ReloopReady.makeButtonDownInputHandler(function() { - this.inSetValue(engine.getValue(this.group, "loop_size")); - }); - }, - trigger: function() { - this.output(); - }, - }); - - this.pads.forEach((pad, i) => pad.midi = [0x94 + index, 0x14 + i]); + this.pads = this.pads.map(control => new components.Button({ + midi: [0x94 + deckIdx, control], + group: `[Channel${deckIdx + 1}]`, + shiftOffset: 0x8, + off: ReloopReady.padColorPalette.Red.dim, + on: ReloopReady.padColorPalette.Red.lit, + outConnect: false, + })); - this.parameterLeft = new components.Button({ - midi: [0x94 + index, 0x28], - shiftOffset: 0x2, - input: ReloopReady.makeButtonDownInputHandler(function() { - loopMoveBeats *= 0.5; - }), - }); - this.parameterRight = new components.Button({ - midi: [0x94 + index, 0x29], - shiftOffset: 0x2, - input: ReloopReady.makeButtonDownInputHandler(function() { - loopMoveBeats *= 2; - }), - }); + this.parameterLeft = new components.Button({ + midi: [0x94 + deckIdx, 0x28], //shifted control: 0x2A + shiftOffset: 0x2, + input: this.makeParameterInputHandler(-1), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + deckIdx, 0x29], //shifted control: 0x2B + shiftOffset: 0x2, + input: this.makeParameterInputHandler(1), + }); - this.reconnectComponents(c => { - if (c.group === undefined) { - c.group = group; - } - }); + this.setLoopSizes(this.currentLoopSizeExp); + } }; -ReloopReady.ManualLoopPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); +ReloopReady.ManualLoopPadMode = class extends ReloopReady.PadMode { + constructor(deckIdx) { + super(deckIdx); + let loopMoveBeats = 4; + const group = `[Channel${deckIdx + 1}]`; -ReloopReady.SamplerPadMode = function(index) { - ReloopReady.PadMode.call(this, index); + this.pads[0] = new components.Button({ + key: "loop_in", + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + }); + this.pads[1] = new components.Button({ + key: "loop_out", + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + }); + this.pads[2] = new components.Button({ + inKey: "reloop_toggle", + outKey: "loop_enabled", + on: ReloopReady.padColorPalette.Red.lit, + off: ReloopReady.padColorPalette.Red.dim, + }); + this.pads[3] = new components.Button({ + key: "loop_in_goto", + on: ReloopReady.padColorPalette.Red.lit, + off: ReloopReady.padColorPalette.Red.dim, + }); + this.pads[4] = new components.Button({ + key: "loop_halve", + on: ReloopReady.padColorPalette.Green.lit, + off: ReloopReady.padColorPalette.Green.dim, + }); + this.pads[5] = new components.Button({ + key: "loop_double", + on: ReloopReady.padColorPalette.Green.lit, + off: ReloopReady.padColorPalette.Green.dim, + }); + this.pads[6] = new components.Button({ + inKey: "loop_move", + on: ReloopReady.padColorPalette.Cyan.lit, + off: ReloopReady.padColorPalette.Cyan.dim, + unshift: function() { + this.input = ReloopReady.makeButtonDownInputHandler(function() { + this.inSetValue(-loopMoveBeats); + }); + }, + shift: function() { + this.input = ReloopReady.makeButtonDownInputHandler(function() { + this.inSetValue(-engine.getValue(this.group, "loop_size")); + }); + }, + trigger: ReloopReady.makeIsPressedTrigger(), + }); + this.pads[7] = new components.Button({ + inKey: "loop_move", + on: ReloopReady.padColorPalette.Cyan.lit, + off: ReloopReady.padColorPalette.Cyan.dim, + unshift: function() { + this.input = ReloopReady.makeButtonDownInputHandler(function() { + this.inSetValue(loopMoveBeats); + }); + }, + shift: function() { + this.input = ReloopReady.makeButtonDownInputHandler(function() { + this.inSetValue(engine.getValue(this.group, "loop_size")); + }); + }, + trigger: ReloopReady.makeIsPressedTrigger(), + }); + + this.pads.forEach((pad, i) => pad.midi = [0x94 + deckIdx, 0x14 + i]); - // var baseOffset = index * 8; + this.parameterLeft = new components.Button({ + midi: [0x94 + deckIdx, 0x28], + shiftOffset: 0x2, + input: ReloopReady.makeButtonDownInputHandler(function() { + loopMoveBeats *= 0.5; + }), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + deckIdx, 0x29], + shiftOffset: 0x2, + input: ReloopReady.makeButtonDownInputHandler(function() { + loopMoveBeats *= 2; + }), + }); - this.pads = this.pads.map((_, i) => - new components.SamplerButton({ - midi: [0x94 + index, 0x14 + i], - // number: baseOffset + i + 1, + this.reconnectComponents(c => { + if (c.group === undefined) { + c.group = group; + } + }); + } +}; +ReloopReady.SamplerPadMode = class extends ReloopReady.PadMode { + constructor(deckIdx) { + super(deckIdx); + this.pads = this.pads.map((control, i) => new components.SamplerButton({ + midi: [0x94 + deckIdx, control], number: i + 1, shiftOffset: 0x8, off: 0x00, outConnect: false, - }) - ); + })); + } }; -ReloopReady.SamplerPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); - - -ReloopReady.LoopRollPadMode = function(index) { - ReloopReady.PadMode.call(this); - - this.currentLoopSizeExp = -2; - const theContainer = this; - - const clampLoopSizeExp = loopSizeExp => _.clamp(loopSizeExp, -5, -2); - - this.setLoopSizes = loopSizeExp => { - _(_.range(loopSizeExp, loopSizeExp + 8)) - .map(function(exp) { return Math.pow(2, exp); }) - .zip(this.pads) - .forEach(([size, pad]) => { - pad.inKey = `beatlooproll_${size}_activate`; - pad.outKey = `beatloop_${size}_enabled`; - }); - }; - - for (let i = 0; i < this.pads.length; i++) { - this.pads[i] = new components.Button({ - midi: [0x94 + index, 0x14 + i], - group: `[Channel${index + 1}]`, - shiftOffset: 0x8, - off: ReloopReady.padColorPalette.Green.dim, - on: ReloopReady.padColorPalette.Green.lit, - outConnect: false, - }); +ReloopReady.LoopRollPadMode = class extends ReloopReady.AbstractLoopPadMode { + setLoopSizeProperties([size, pad]) { + pad.inKey = `beatlooproll_${size}_activate`; + pad.outKey = `beatloop_${size}_enabled`; } - const makeParameterInputHandler = loopSizeChangeAmount => - ReloopReady.makeButtonDownInputHandler(function() { - const newLoopSize = clampLoopSizeExp(theContainer.currentLoopSizeExp + loopSizeChangeAmount); + makeParameterInputHandler(loopSizeChangeAmount) { + const theContainer = this; + return ReloopReady.makeButtonDownInputHandler(function() { + const newLoopSize = theContainer.clampLoopSizeExp(theContainer.currentLoopSizeExp + loopSizeChangeAmount); if (newLoopSize !== theContainer.currentLoopSizeExp) { theContainer.currentLoopSizeExp = newLoopSize; theContainer.setLoopSizes(newLoopSize); theContainer.reconnectComponents(); } }); + } - this.parameterLeft = new components.Button({ - midi: [0x94 + index, 0x28], - shiftOffset: 0x2, - input: makeParameterInputHandler(-1), - }); - this.parameterRight = new components.Button({ - midi: [0x94 + index, 0x29], - shiftOffset: 0x2, - input: makeParameterInputHandler(1), - }); + constructor(deckIdx) { + super(deckIdx); + this.currentLoopSizeExp = -2; - this.setLoopSizes(this.currentLoopSizeExp); + this.pads = this.pads.map(control => new components.Button({ + midi: [0x94 + deckIdx, control], + group: `[Channel${deckIdx + 1}]`, + shiftOffset: 0x8, + off: ReloopReady.padColorPalette.Green.dim, + on: ReloopReady.padColorPalette.Green.lit, + outConnect: false, + })); + } }; -ReloopReady.LoopRollPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); +// Pitch Play mode taken and heavily modified from Roland DJ 505 mapping. +ReloopReady.Pitch = class extends ReloopReady.PadMode { + constructor(deckIdx) { + super(deckIdx); + const PitchPlayRange = { + UP: 0, + MID: 1, + DOWN: 2, + }; -// Pitch Play mode taken from Roland DJ 505 mapping. -ReloopReady.Pitch = function(index) { - components.ComponentContainer.call(this); - - const PitchPlayRange = { - UP: 0, - MID: 1, - DOWN: 2, - }; + //this.ledControl = DJ505.PadMode.SAMPLER; + const color = ReloopReady.padColorPalette.Purple; + let cuepoint = 1; + let range = PitchPlayRange.MID; + const theContainer = this; + + this.PerformancePad = function(n) { + this.midi = [0x94 + deckIdx, 0x14 + n]; + this.number = n + 1; + this.on = ReloopReady.padColorPalette.Purple.dim; + this.colorMapper = ReloopReady.padColorMapper; + this.colorKey = `hotcue_${this.number}_color`; + components.Button.call(this); + }; + this.PerformancePad.prototype = new components.Button({ + shiftOffset: 8, + group: `[Channel${deckIdx + 1}]`, + outConnect: false, + off: ReloopReady.padColorPalette.Off.lit, + outputColor: function(colorCode) { + // For colored hotcues (shifted only) + const midiColor = this.colorMapper.getValueForNearestColor(colorCode); + this.send((cuepoint === this.number) ? midiColor : ReloopReady.dimColor(midiColor)); + }, + unshift: function() { + this.outKey = "pitch_adjust"; + this.output = function(_value, _group, _control) { + let midiColor = color.dim; + if ((range === PitchPlayRange.UP && this.number === 5) || + (range === PitchPlayRange.MID && this.number === 1) || + (range === PitchPlayRange.DOWN && this.number === 4)) { + midiColor = ReloopReady.padColorPalette.White.lit; + } + this.send(midiColor); + }; + this.input = function(_channel, _control, value, _status, _group) { + const pitchAdjust = (function() { + switch (range) { + case PitchPlayRange.UP: + return this.number + ((this.number <= 4) ? 4 : -5); + case PitchPlayRange.MID: + return this.number - ((this.number <= 4) ? 1 : 9); + case PitchPlayRange.DOWN: + return this.number - ((this.number <= 4) ? 4 : 12); + } + }).call(this); + engine.setValue(this.group, "pitch_adjust", pitchAdjust); + if (engine.getValue(this.group, `hotcue_${cuepoint}_type`) && engine.getValue(this.group, "play")) { + engine.setValue(this.group, `hotcue_${cuepoint}_goto`, value); + } else { + engine.setValue(this.group, `hotcue_${cuepoint}_activate`, value); + } + }; + this.connect = function() { + components.Button.prototype.connect.call(this); // call parent connect - //this.ledControl = DJ505.PadMode.SAMPLER; - const color = ReloopReady.padColorPalette.Purple; - let cuepoint = 1; - let range = PitchPlayRange.MID; - const theContainer = this; - - this.PerformancePad = function(n) { - this.midi = [0x94 + index, 0x14 + n]; - this.number = n + 1; - this.on = ReloopReady.padColorPalette.Purple.dim; - this.colorMapper = ReloopReady.padColorMapper; - this.colorKey = `hotcue_${this.number}_color`; - components.Button.call(this); - }; - this.PerformancePad.prototype = new components.Button({ - shiftOffset: 8, - group: `[Channel${index + 1}]`, - outConnect: false, - off: ReloopReady.padColorPalette.Off.lit, - outputColor: function(colorCode) { - // For colored hotcues (shifted only) - const midiColor = this.colorMapper.getValueForNearestColor(colorCode); - this.send((cuepoint === this.number) ? midiColor : ReloopReady.dimColor(midiColor)); - }, - unshift: function() { - this.outKey = "pitch_adjust"; - this.output = function(_value, _group, _control) { - let midiColor = color.dim; - if ((range === PitchPlayRange.UP && this.number === 5) || - (range === PitchPlayRange.MID && this.number === 1) || - (range === PitchPlayRange.DOWN && this.number === 4)) { - midiColor = ReloopReady.padColorPalette.White.lit; + if (this.connections[1] !== undefined) { + // Necessary, since trigger() apparently also triggers disconnected connections + this.connections.pop(); + } + }; + if (this.connections[0] !== undefined) { + this.disconnect(); + this.connect(); + this.trigger(); } - this.send(midiColor); - }; - this.input = function(_channel, _control, value, _status, _group) { - const pitchAdjust = (function() { - switch (range) { - case PitchPlayRange.UP: - return this.number + ((this.number <= 4) ? 4 : -5); - case PitchPlayRange.MID: - return this.number - ((this.number <= 4) ? 1 : 9); - case PitchPlayRange.DOWN: - return this.number - ((this.number <= 4) ? 4 : 12); + }, + shift: function() { + this.outKey = `hotcue_${this.number}_enabled`; + this.output = function(value, _group, _control) { + const outval = this.outValueScale(value); + if (this.colorKey !== undefined && outval !== this.off) { + this.outputColor(engine.getValue(this.group, this.colorKey)); + } else { + this.send(ReloopReady.padColorPalette.Off.lit); } - }).call(this); - engine.setValue(this.group, "pitch_adjust", pitchAdjust); - engine.setValue(this.group, `hotcue_${cuepoint}_activate`, value); - }; - this.connect = function() { - components.Button.prototype.connect.call(this); // call parent connect - - if (this.connections[1] !== undefined) { - // Necessary, since trigger() apparently also triggers disconnected connections - this.connections.pop(); + }; + this.input = function(_channel, _control, value, _status, _group) { + if (value > 0 && cuepoint !== this.number && engine.getValue(this.group, `hotcue_${this.number}_enabled`)) { + const previousCuepoint = cuepoint; + cuepoint = this.number; + theContainer.pads[previousCuepoint - 1].trigger(); + this.outputColor(engine.getValue(this.group, this.colorKey)); + } + }; + this.connect = function() { + components.Button.prototype.connect.call(this); // call parent connect + if (undefined !== this.group && this.colorKey !== undefined) { + this.connections[1] = engine.makeConnection(this.group, this.colorKey, function(id) { + if (engine.getValue(this.group, this.outKey)) { + this.outputColor(id); + } + }); + } + }; + if (this.connections[0] !== undefined) { + this.disconnect(); + this.connect(); + this.trigger(); } - }; - if (this.connections[0] !== undefined) { - this.disconnect(); - this.connect(); - this.trigger(); - } - }, - shift: function() { - this.outKey = `hotcue_${this.number}_enabled`; - this.output = function(value, _group, _control) { - const outval = this.outValueScale(value); - if (this.colorKey !== undefined && outval !== this.off) { - this.outputColor(engine.getValue(this.group, this.colorKey)); + }, + }); + this.pads = new components.ComponentContainer(); + for (let n = 0; n <= 7; n++) { + this.pads[n] = new this.PerformancePad(n); + } + + this.parameterLeft = new components.Button({ + midi: [0x94 + deckIdx, 0x28], + shiftOffset: 0x2, + input: ReloopReady.makeButtonDownInputHandler(function() { + if (range === PitchPlayRange.UP) { + range = PitchPlayRange.MID; + } else if (range === PitchPlayRange.MID) { + range = PitchPlayRange.DOWN; } else { - this.send(ReloopReady.padColorPalette.Off.lit); + range = PitchPlayRange.UP; } - }; - this.input = function(_channel, _control, value, _status, _group) { - if (value > 0 && cuepoint !== this.number && engine.getValue(this.group, `hotcue_${this.number}_enabled`)) { - const previousCuepoint = cuepoint; - cuepoint = this.number; - theContainer.pads[previousCuepoint - 1].trigger(); - this.outputColor(engine.getValue(this.group, this.colorKey)); - } - }; - this.connect = function() { - components.Button.prototype.connect.call(this); // call parent connect - if (undefined !== this.group && this.colorKey !== undefined) { - this.connections[1] = engine.makeConnection(this.group, this.colorKey, function(id) { - if (engine.getValue(this.group, this.outKey)) { - this.outputColor(id); - } - }); + theContainer.pads.forEachComponent(c => c.trigger()); + }), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + deckIdx, 0x29], + shiftOffset: 0x2, + input: ReloopReady.makeButtonDownInputHandler(function() { + if (range === PitchPlayRange.UP) { + range = PitchPlayRange.DOWN; + } else if (range === PitchPlayRange.MID) { + range = PitchPlayRange.UP; + } else { + range = PitchPlayRange.MID; } - }; - if (this.connections[0] !== undefined) { - this.disconnect(); - this.connect(); - this.trigger(); - } - }, - }); - this.pads = new components.ComponentContainer(); - for (let n = 0; n <= 7; n++) { - this.pads[n] = new this.PerformancePad(n); + theContainer.pads.forEachComponent(c => c.trigger()); + }), + }); } - - this.parameterLeft = new components.Button({ - midi: [0x94 + index, 0x28], - shiftOffset: 0x2, - input: ReloopReady.makeButtonDownInputHandler(function() { - if (range === PitchPlayRange.UP) { - range = PitchPlayRange.MID; - } else if (range === PitchPlayRange.MID) { - range = PitchPlayRange.DOWN; - } else { - range = PitchPlayRange.UP; - } - theContainer.forEachComponent(c => c.trigger()); - }), - }); - this.parameterRight = new components.Button({ - midi: [0x94 + index, 0x29], - shiftOffset: 0x2, - input: ReloopReady.makeButtonDownInputHandler(function() { - if (range === PitchPlayRange.UP) { - range = PitchPlayRange.DOWN; - } else if (range === PitchPlayRange.MID) { - range = PitchPlayRange.UP; - } else { - range = PitchPlayRange.MID; - } - theContainer.forEachComponent(c => c.trigger()); - }), - }); }; -ReloopReady.Pitch.prototype = Object.create(ReloopReady.PadMode.prototype); - - // There is no such thing as a scratch bank in Mixxx so I'm repurpusing this // PadMode for beatjumping. -ReloopReady.ScratchBankPadMode = function(index) { - ReloopReady.PadMode.call(this); - - this.currentJumpSizeExp = -2; - const theContainer = this; - - const clampJumpSizeExp = jumpSizeExp => _.clamp(jumpSizeExp, -5, 6); - - this.setjumpSizeExp = jumpSizeExp => { +ReloopReady.ScratchBankPadMode = class extends ReloopReady.PadMode { + clampJumpSizeExp(jumpSizeExp) { + return _.clamp(jumpSizeExp, -5, 6); + } + setjumpSizeExp(jumpSizeExp) { const middle = this.pads.length / 2; let jumpsize = Math.pow(2, jumpSizeExp); for (let i = 0; i < middle; ++i) { const padTop = this.pads[i]; padTop.inKey = `beatjump_${jumpsize}_forward`; padTop.outKey = padTop.inKey; - const padBot = this.pads[i + middle]; + const padBot = this.pads[middle + i]; padBot.inKey = `beatjump_${jumpsize}_backward`; padBot.outKey = padBot.inKey; jumpsize *= 2; } - }; - - - for (let i = 0; i < this.pads.length; i++) { - this.pads[i] = new components.Button({ - midi: [0x94 + index, 0x14 + i], - group: `[Channel${index + 1}]`, - shiftOffset: 0x8, - off: ReloopReady.padColorPalette.Cyan.dim, - on: ReloopReady.padColorPalette.Cyan.lit, - outConnect: false, - }); } - - const makeParameterInputHandler = jumpSizeChangeAmount => - ReloopReady.makeButtonDownInputHandler(function() { - const newJumpSize = clampJumpSizeExp(theContainer.currentJumpSizeExp + jumpSizeChangeAmount); + makeParameterInputHandler(jumpSizeChangeAmount) { + const theContainer = this; + return ReloopReady.makeButtonDownInputHandler(function() { + const newJumpSize = theContainer.clampJumpSizeExp(theContainer.currentJumpSizeExp + jumpSizeChangeAmount); if (newJumpSize !== theContainer.currentJumpSizeExp) { theContainer.currentJumpSizeExp = newJumpSize; theContainer.setjumpSizeExp(newJumpSize); theContainer.reconnectComponents(); } }); + } + constructor(deckIdx) { + super(deckIdx); + + this.currentJumpSizeExp = -2; + + this.pads = this.pads.map(control => + new components.Button({ + midi: [0x94 + deckIdx, control], + group: `[Channel${deckIdx + 1}]`, + shiftOffset: 0x8, + off: ReloopReady.padColorPalette.Cyan.dim, + on: ReloopReady.padColorPalette.Cyan.lit, + outConnect: false, + }) + ); + + this.parameterLeft = new components.Button({ + midi: [0x94 + deckIdx, 0x28], + shiftOffset: 0x2, + input: this.makeParameterInputHandler(-1), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + deckIdx, 0x29], + shiftOffset: 0x2, + input: this.makeParameterInputHandler(1), + }); - this.parameterLeft = new components.Button({ - midi: [0x94 + index, 0x28], - shiftOffset: 0x2, - input: makeParameterInputHandler(-1), - }); - this.parameterRight = new components.Button({ - midi: [0x94 + index, 0x29], - shiftOffset: 0x2, - input: makeParameterInputHandler(1), - }); - - this.setjumpSizeExp(this.currentJumpSizeExp); + this.setjumpSizeExp(this.currentJumpSizeExp); + } }; +ReloopReady.BeatGridPadMode = class extends ReloopReady.PadMode { + constructor(deckIdx) { + super(deckIdx); -ReloopReady.ScratchBankPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); - -ReloopReady.BeatGridPadMode = function(index) { - ReloopReady.PadMode.call(this); - - const group = `[Channel${index + 1}]`; - - this.pads[0] = new components.Button({ - key: "beats_translate_earlier", - on: ReloopReady.padColorPalette.Blue.lit, - off: ReloopReady.padColorPalette.Blue.dim, - }); - this.pads[1] = new components.Button({ - key: "beats_translate_later", - on: ReloopReady.padColorPalette.Blue.lit, - off: ReloopReady.padColorPalette.Blue.dim, - }); - this.pads[2] = new components.Button({ - key: "beats_adjust_faster", - on: ReloopReady.padColorPalette.Red.lit, - off: ReloopReady.padColorPalette.Red.dim, - trigger: function() { - this.output(); - }, - }); - this.pads[3] = new components.Button({ - key: "beats_adjust_slower", - on: ReloopReady.padColorPalette.Red.lit, - off: ReloopReady.padColorPalette.Red.dim, - trigger: function() { - this.output(); - }, - }); - this.pads[4] = new components.Button({ - key: "shift_cues_earlier", - on: ReloopReady.padColorPalette.Green.lit, - off: ReloopReady.padColorPalette.Green.dim, - trigger: function() { - this.output(); - }, - }); - this.pads[5] = new components.Button({ - key: "shift_cues_later", - on: ReloopReady.padColorPalette.Green.dim, - off: ReloopReady.padColorPalette.Green.dim, - trigger: function() { - this.output(); - }, - }); - this.pads[6] = new components.Button({ - key: "bpm_tap", - on: ReloopReady.padColorPalette.Cyan.dim, - off: ReloopReady.padColorPalette.Cyan.dim, - }); - this.pads[7] = new components.Button({ - key: "beats_translate_curpos", - on: ReloopReady.padColorPalette.Cyan.lit, - off: ReloopReady.padColorPalette.Cyan.dim, - }); + const group = `[Channel${deckIdx + 1}]`; - this.pads.forEach((pad, i) => pad.midi = [0x94 + index, 0x14 + i]); + this.pads[0] = new components.Button({ + key: "beats_translate_earlier", + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + }); + this.pads[1] = new components.Button({ + key: "beats_translate_later", + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + }); + this.pads[2] = new components.Button({ + key: "beats_adjust_faster", + on: ReloopReady.padColorPalette.Red.lit, + off: ReloopReady.padColorPalette.Red.dim, + trigger: function() { + this.output(); + }, + }); + this.pads[3] = new components.Button({ + key: "beats_adjust_slower", + on: ReloopReady.padColorPalette.Red.lit, + off: ReloopReady.padColorPalette.Red.dim, + trigger: function() { + this.output(); + }, + }); + this.pads[4] = new components.Button({ + key: "shift_cues_earlier", + on: ReloopReady.padColorPalette.Green.lit, + off: ReloopReady.padColorPalette.Green.dim, + trigger: function() { + this.output(); + }, + }); + this.pads[5] = new components.Button({ + key: "shift_cues_later", + on: ReloopReady.padColorPalette.Green.lit, + off: ReloopReady.padColorPalette.Green.dim, + trigger: function() { + this.output(); + }, + }); + this.pads[6] = new components.Button({ + key: "bpm_tap", + on: ReloopReady.padColorPalette.Cyan.lit, + off: ReloopReady.padColorPalette.Cyan.dim, + }); + this.pads[7] = new components.Button({ + key: "beats_translate_curpos", + on: ReloopReady.padColorPalette.Cyan.lit, + off: ReloopReady.padColorPalette.Cyan.dim, + }); - this.parameterLeft = new components.Button({ - midi: [0x94 + index, 0x28], - shiftOffset: 0x2 - }); - this.parameterRight = new components.Button({ - midi: [0x94 + index, 0x29], - shiftOffset: 0x2 - }); + this.pads.forEach((pad, i) => pad.midi = [0x94 + deckIdx, 0x14 + i]); - this.reconnectComponents(c => { - if (c.group === undefined) { - c.group = group; - } - }); + this.reconnectComponents(c => { + if (c.group === undefined) { + c.group = group; + } + }); + } }; -ReloopReady.BeatGridPadMode.prototype = Object.create(ReloopReady.PadMode.prototype); - -// Ordering of array elements determines layout of pad mode selectors. ReloopReady.controlPadModeAssoc = [ {pos: 0, control: 0x00, mode: ReloopReady.HotcuePadMode}, {pos: 1, control: 0x05, mode: ReloopReady.AutoLoopPadMode}, @@ -964,29 +949,65 @@ ReloopReady.controlPadModeAssoc = [ {pos: 6, control: 0x08, mode: ReloopReady.LoopRollPadMode}, {pos: 7, control: 0x12, mode: ReloopReady.BeatGridPadMode}, ]; +ReloopReady.PadContainer = class extends components.ComponentContainer { + constructor(deckIdx) { + super(); + + // construct instance of each mode per Container + this.padModeInstances = ReloopReady.controlPadModeAssoc.map(obj => { + const modeInstance = new (obj.mode)(deckIdx); + // make sure no multiple components have "ownership" over each pad + modeInstance.forEachComponent(c => c.disconnect()); + return { + pos: obj.pos, + control: obj.control, + modeInstance: modeInstance, + }; + }); -ReloopReady.PadContainer = function(index) { + this.currentLayer = this.padModeInstances[0].modeInstance; + this.currentLayer.reconnectComponents(); - // parent constructor is called later + const thisContainer = this; - components.ComponentContainer.call(this); + // factory/HO function for creating input handlers that change the padmode + // this expects to be used as for creating the input handler of a + // components.Button + const makePadModeInputHandler = layer => (layer === undefined) ? + ReloopReady.noopInputHandler : + function(channel, control, value, status, _group) { + if (!this.isPress(channel, control, value, status)) { + return; + } + thisContainer.applyLayer(layer); + }; - // construct instance of each mode per Container - const padModeInstances = ReloopReady.controlPadModeAssoc.map(obj => { - const modeInstance = new (obj.mode)(index); - // make sure no multiple components have "ownership" over each pad - modeInstance.forEachComponent(c => c.disconnect()); - return { - pos: obj.pos, - control: obj.control, - modeInstance: modeInstance, - }; - }); - this.currentLayer = padModeInstances[0].modeInstance; - this.currentLayer.reconnectComponents(); + // create physical buttons for changing the layers + this.padModeSelectors = Array(8); + this.padModeInstances.forEach(obj => { + thisContainer.padModeSelectors[obj.pos] = new components.Button({ + midi: [0x94 + deckIdx, obj.control], + on: ReloopReady.padColorPalette.Blue.lit, + off: ReloopReady.padColorPalette.Blue.dim, + input: makePadModeInputHandler(obj.modeInstance), + trigger: function() { + this.output(thisContainer.currentLayer === obj.modeInstance); + } + }); + }); + this.padModeSelectors = this.padModeSelectors.map(obj => obj === undefined ? new components.Button({}) : obj + ); + + // button can not be controlled from software. + // This component instance just serves as input handler for debugging. + this.modeButton = new components.Button({ + midi: [deckIdx, 0x20], // shifted: [0x0C + deckIdx, 0x11], shifted led is controllable, unshifted is not. + input: ReloopReady.noopInputHandler + }); + } - const applyLayer = layer => { + applyLayer(layer) { if (this.currentLayer === layer) { return; } @@ -1006,136 +1027,120 @@ ReloopReady.PadContainer = function(index) { this.currentLayer = layer; this.padModeSelectors.forEach(selector => selector.trigger()); - }; - - // factory/HO function for creating input handlers that change the padmode - // this expects to be used as for creating the input handler of a - // components.Button - const makePadModeInputHandler = layer => - (layer === undefined) ? - ReloopReady.noopInputHandler : - function(channel, control, value, status, _group) { - if (!this.isPress(channel, control, value, status)) { - return; - } - applyLayer(layer); - }; - - const thisContainer = this; + } - // create physical buttons for changing the layers - this.padModeSelectors = Array(8); - padModeInstances.forEach(obj => { - thisContainer.padModeSelectors[obj.pos] = new components.Button({ - midi: [0x94 + index, obj.control], - on: ReloopReady.padColorPalette.Blue.lit, - off: ReloopReady.padColorPalette.Blue.dim, - input: makePadModeInputHandler(obj.modeInstance), - trigger: function() { - this.output(thisContainer.currentLayer === obj.modeInstance); - } - }); - }); - this.padModeSelectors = this.padModeSelectors.map(obj => - obj === undefined ? new components.Button({}) : obj - ); - - // button can not be controlled from software. - // This component instance just serves as input handler for debugging. - this.modeButton = new components.Button({ - midi: [index, 0x20], // shifted: [0x0C + index, 0x11], shifted led is controllable, unshifted is not. - input: ReloopReady.noopInputHandler - }); }; +ReloopReady.Deck = class extends components.Deck { + constructor(index) { -ReloopReady.PadContainer.prototype = Object.create(components.ComponentContainer.prototype); + const channel = index + 1; -ReloopReady.Deck = function(index) { + super(channel); - const channel = index + 1; + const thisDeck = this; + const midiOn = 0x90 + index; - components.Deck.call(this, channel); - const thisDeck = this; - const midiOn = 0x90 + index; + this.play = new components.PlayButton({ + midi: [midiOn, 0x00], + shiftOffset: 0x10, + shift: function() { + // match behavior labelled on hardware + this.inKey = "reverseroll"; + // todo fix interaction with shift button + }, + }); + // needs matching cuemode to be used + this.cue = new components.CueButton({ + midi: [midiOn, 0x01], + shiftOffset: 0x04, + }); - this.play = new components.PlayButton({ - midi: [midiOn, 0x00], - shiftOffset: 0x10, - shift: function() { - // match behavior labelled on hardware - this.inKey = "reverseroll"; - // todo fix interaction with shift button - }, - }); + this.sync = new components.SyncButton({ + midi: [midiOn, 0x02], + shiftOffset: 0x1, + }); // TODO investigate whether custom behavior is required to match controller - // needs matching cuemode to be used - this.cue = new components.CueButton({ - midi: [midiOn, 0x01], - shiftOffset: 0x04, - }); + this.vinyl = new components.Button({ + midi: [midiOn, 0x0F], + shiftOffset: -0x08, + type: components.Button.prototype.types.toggle, + shift: function() { + this.inKey = "slip_enabled"; + this.outKey = this.inKey; + this.input = components.Button.prototype.input; + }, + unshift: function() { + // prevent binding to slip_enabled when unshifted + // (otherwise vinyl led would glitch when a new track is loaded). + this.inKey = this.outKey = undefined; + this.input = function(channel, control, value, status, _group) { + if (this.isPress(channel, control, value, status)) { + thisDeck.jog.vinylMode = !thisDeck.jog.vinylMode; + this.output(thisDeck.jog.vinylMode); + } + }; + }, + trigger: function() { + this.output(thisDeck.jog.vinylMode); + } + }); - this.sync = new components.SyncButton({ - midi: [midiOn, 0x02], - shiftOffset: 0x1, - }); // TODO investigate whether custom behavior is required to match controller - - this.vinyl = new components.Button({ - midi: [midiOn, 0x0F], - shiftOffset: -0x08, - type: components.Button.prototype.types.toggle, - shift: function() { - this.inKey = "slip_enabled"; - this.outKey = this.inKey; - this.input = components.Button.prototype.input; - }, - unshift: function() { - this.input = function(channel, control, value, status, _group) { - if (this.isPress(channel, control, value, status)) { - thisDeck.jog.vinylMode = ! thisDeck.jog.vinylMode; - this.output(thisDeck.jog.vinylMode); + this.jog = new components.JogWheelBasic({ + // midiTouch: [0x90 + index, 0x06] + // midiWheel: [0xB0 + index, 0x06] + alpha: 1 / 8, + deck: channel, + // if we shift during scratching, takeover the new settings + // immideatily + retriggerScratch: function() { + if (engine.isScratching(this.deck) && this.vinylMode) { + engine.scratchDisable(this.deck); + engine.scratchEnable(this.deck, + this.wheelResolution, + this.rpm, + this.alpha, + this.beta); } - }; - }, - trigger: function() { - this.output(thisDeck.jog.vinylMode); - } - }); - - this.jog = new components.JogWheelBasic({ - // midiTouch: [0x90 + index, 0x06] - // midiWheel: [0xB0 + index, 0x06] - alpha: 1/8, - deck: channel, - wheelResolution: 300, - }); + }, + unshift: function() { + // accurate, physical control will match spinny. + this.wheelResolution = 300; + this.retriggerScratch(); + }, + shift: function() { + // slow down wheel to 1/3rd the speed + // to allow for finer adjustments. + this.wheelResolution = 900; + this.retriggerScratch(); + } + }); - this.keylock = new components.Button({ - midi: [midiOn, 0x0D], - shiftOffset: 0x1C, - shift: function() { - this.type = components.Button.prototype.types.push; - this.inKey = "sync_key"; - this.outKey = this.inKey; - }, - unshift: function() { - this.type = components.Button.prototype.types.toggle; - this.inKey = "keylock"; - this.outKey = this.inKey; - } - }); + this.keylock = new components.Button({ + midi: [midiOn, 0x0D], + shiftOffset: 0x1C, + shift: function() { + this.type = components.Button.prototype.types.push; + this.inKey = "sync_key"; + this.outKey = this.inKey; + }, + unshift: function() { + this.type = components.Button.prototype.types.toggle; + this.inKey = "keylock"; + this.outKey = this.inKey; + } + }); - this.fxUnit = new ReloopReady.FxUnit(index); + this.fxUnit = new ReloopReady.FxUnit(index); - this.padUnit = new ReloopReady.PadContainer(index); + this.padUnit = new ReloopReady.PadContainer(index); - this.reconnectComponents(function(c) { - if (c.group === undefined) { - c.group = thisDeck.currentDeck; - } - }); + this.reconnectComponents(function(c) { + if (c.group === undefined) { + c.group = thisDeck.currentDeck; + } + }); + } }; - -ReloopReady.Deck.prototype = new components.Deck(); From 74a6a04435940cdd97903cf11eedc27f4fefdccf Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Thu, 22 Feb 2024 23:27:28 +0100 Subject: [PATCH 03/18] chore: fix eslint --- res/controllers/Reloop-Ready-scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 666b593f8df..b81f8633f52 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -136,7 +136,7 @@ class ReloopReady { return; } console.warn(`unregonized incoming sysex data: ${data}`); - }; + } shutdown() { this.components.shutdown(); From 58f5aa14ee8d1489f3c35b5a7daa470c7147d0ad Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:14:17 +0100 Subject: [PATCH 04/18] fix: LoopRollPadMode not working --- res/controllers/Reloop-Ready-scripts.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index b81f8633f52..39ccd2a77a3 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -659,6 +659,19 @@ ReloopReady.LoopRollPadMode = class extends ReloopReady.AbstractLoopPadMode { on: ReloopReady.padColorPalette.Green.lit, outConnect: false, })); + + this.parameterLeft = new components.Button({ + midi: [0x94 + deckIdx, 0x28], //shifted control: 0x2A + shiftOffset: 0x2, + input: this.makeParameterInputHandler(-1), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + deckIdx, 0x29], //shifted control: 0x2B + shiftOffset: 0x2, + input: this.makeParameterInputHandler(1), + }); + + this.setLoopSizes(this.currentLoopSizeExp); } }; From 95e77e2abdd6a705598942871860a257315a800f Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:42:13 +0100 Subject: [PATCH 05/18] refactor: implement parameter buttons for all `AbstractLoopPadMode`s --- res/controllers/Reloop-Ready-scripts.js | 33 +++++++++++++++---------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 39ccd2a77a3..cb3d6cda191 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -431,7 +431,7 @@ ReloopReady.PadMode = class extends components.ComponentContainer { } shutdown() { // we want the pads to be completely off when shutdown, not dimmed - this.pads.forEach(pad => pad.send(ReloopReady.singleColorLED.off,)); + this.pads.forEach(pad => pad.send(ReloopReady.singleColorLED.off)); this.parameterLeft.shutdown(); this.parameterRight.shutdown(); } @@ -485,6 +485,25 @@ ReloopReady.AbstractLoopPadMode = class extends ReloopReady.PadMode { if (new.target === ReloopReady.AbstractLoopPadMode) { throw new TypeError("Cannot construct AbstractLoopPadMode instances directly"); } + + const trigger = function() { + this.send(this.off); + }; + + // Loop Modes can always have their Parameter buttons + // implemented in terms of `makeParameterInputHandler` so do that here. + this.parameterLeft = new components.Button({ + midi: [0x94 + deckIdx, 0x28], //shifted control: 0x2A + shiftOffset: 0x2, + input: this.makeParameterInputHandler(-1), + trigger: trigger, + }); + this.parameterRight = new components.Button({ + midi: [0x94 + deckIdx, 0x29], //shifted control: 0x2B + shiftOffset: 0x2, + input: this.makeParameterInputHandler(1), + trigger: trigger, + }); } }; ReloopReady.AutoLoopPadMode = class extends ReloopReady.AbstractLoopPadMode { @@ -507,18 +526,6 @@ ReloopReady.AutoLoopPadMode = class extends ReloopReady.AbstractLoopPadMode { on: ReloopReady.padColorPalette.Red.lit, outConnect: false, })); - - this.parameterLeft = new components.Button({ - midi: [0x94 + deckIdx, 0x28], //shifted control: 0x2A - shiftOffset: 0x2, - input: this.makeParameterInputHandler(-1), - }); - this.parameterRight = new components.Button({ - midi: [0x94 + deckIdx, 0x29], //shifted control: 0x2B - shiftOffset: 0x2, - input: this.makeParameterInputHandler(1), - }); - this.setLoopSizes(this.currentLoopSizeExp); } }; From a31216e776b32e5fc41a59e36d0cae45eb14650d Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:49:21 +0100 Subject: [PATCH 06/18] refactor: optimize ColorMapper initialization --- res/controllers/Reloop-Ready-scripts.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index cb3d6cda191..6c5abf3962b 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -224,10 +224,7 @@ ReloopReady.noopInputHandler = (_channel, _control, _value, _status, _group) => (() => { // rewrite the following without lodash - const fullColorToMidiColorMap = _(_.range(0x00, 0x80)) - .keyBy() - .mapKeys(ReloopReady.midiToFullColor) - .value(); + const fullColorToMidiColorMap = _.keyBy(_.range(0x00, 0x80), ReloopReady.midiToFullColor); ReloopReady.padColorMapper = new ColorMapper(fullColorToMidiColorMap); From bd4d1fb8d627b14764e6edca5a99830a8245da93 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Mon, 30 Dec 2024 21:05:01 +0100 Subject: [PATCH 07/18] wip --- res/controllers/Reloop Ready.midi.xml | 38 +++++++++++++++++++++++++ res/controllers/Reloop-Ready-scripts.js | 10 +++++++ 2 files changed, 48 insertions(+) diff --git a/res/controllers/Reloop Ready.midi.xml b/res/controllers/Reloop Ready.midi.xml index 235cf5206ca..d6d099a3d6c 100644 --- a/res/controllers/Reloop Ready.midi.xml +++ b/res/controllers/Reloop Ready.midi.xml @@ -174,6 +174,25 @@ + + + ReloopReadyInstance.components.leftChannel.load.input + 0x9E + 0x1B + + + + + + + ReloopReadyInstance.components.leftChannel.load.input + 0x8E + 0x1B + + + + + ReloopReadyInstance.components.leftChannel.pfl.input @@ -277,6 +296,25 @@ + + + ReloopReadyInstance.components.rightChannel.load.input + 0x9E + 0x10 + + + + + + + ReloopReadyInstance.components.rightChannel.load.input + 0x8E + 0x10 + + + + + ReloopReadyInstance.components.rightChannel.pfl.input diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 6c5abf3962b..87e59f9f8ac 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -179,6 +179,13 @@ class ReloopReady { return color & 0b00111111; } + static arrayRange(start, stop, step = 1){ + return Array.from( + { length: (stop - start) / step + 1 }, + (_value, index) => start + index * step + ); + } + /** * creates an this.isPress guarded input handler, assumes to be called by components.Button or subclasses thereof @@ -328,6 +335,9 @@ ReloopReady.Channel = class extends components.ComponentContainer { type: components.Button.prototype.types.toggle, }); + // TODO properly handle output messages here + // the constant offset between channels doesn't seem to apply + // left shift: 0x1B, right shift: 0x10 this.load = new components.Button({ midi: [0x9E, 0x02 + deckIdx], shiftOffset: 0x0D, From 03f33e07258747ddd360de4b00850b8450086307 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Mon, 30 Dec 2024 21:34:53 +0100 Subject: [PATCH 08/18] fix: midiToFullColor algorithm --- res/controllers/Reloop-Ready-scripts.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 87e59f9f8ac..63ded6bc467 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -162,16 +162,16 @@ class ReloopReady { * @returns {number} 24-bit (8-bit per channel) upscaled `color` */ static midiToFullColor(color) { - const b = (color & 0b00000011) << 6; - const g = (color & 0b00001100) << 4; - const r = (color & 0b00110000) << 2; + let b = (color & 0b00000011) << 6; + let g = (color & 0b00001100) << 4; + let r = (color & 0b00110000) << 2; const i = (color & 0b01000000) >> 6; if (i === 0) { // half the RGB intensity - r >> 0b1; - g >> 0b1; - b >> 0b1; + r >>= 0b1; + g >>= 0b1; + b >>= 0b1; } return (r << 16) | (g << 8) | b; } From 13146d82ea1e12984a372cd1d1b2bd8e5cf780c4 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Mon, 30 Dec 2024 23:45:15 +0100 Subject: [PATCH 09/18] feat: convert to controller settings GUI --- res/controllers/Reloop Ready.midi.xml | 94 ++++++++++++++++++++++--- res/controllers/Reloop-Ready-scripts.js | 67 +++++------------- 2 files changed, 105 insertions(+), 56 deletions(-) diff --git a/res/controllers/Reloop Ready.midi.xml b/res/controllers/Reloop Ready.midi.xml index d6d099a3d6c..60cb2c53b1c 100644 --- a/res/controllers/Reloop Ready.midi.xml +++ b/res/controllers/Reloop Ready.midi.xml @@ -7,6 +7,84 @@ + + + + + + + + Since the tempo faders are cheaply constructed, on some units the physical indent representing the neutral position does not match the position where the value is actually neutral, this can be corrected here + + + + + + @@ -93,7 +171,7 @@ - ReloopReadyInstance.components.leftChannel.knob1.input + ReloopReadyInstance.components.leftChannel.eqKnob[3].input 0xB0 0x16 @@ -102,7 +180,7 @@ - ReloopReadyInstance.components.leftChannel.knob2.input + ReloopReadyInstance.components.leftChannel.eqKnob[2].input 0xB0 0x17 @@ -111,7 +189,7 @@ - ReloopReadyInstance.components.leftChannel.knob3.input + ReloopReadyInstance.components.leftChannel.eqKnob[1].input 0xB0 0x19 @@ -120,7 +198,7 @@ - ReloopReadyInstance.components.leftChannel.filter.input + ReloopReadyInstance.components.leftChannel.eqKnob[0].input 0xB0 0x1A @@ -215,7 +293,7 @@ - ReloopReadyInstance.components.rightChannel.knob1.input + ReloopReadyInstance.components.rightChannel.eqKnob[3].input 0xB1 0x16 @@ -224,7 +302,7 @@ - ReloopReadyInstance.components.rightChannel.knob2.input + ReloopReadyInstance.components.rightChannel.eqKnob[2].input 0xB1 0x17 @@ -233,7 +311,7 @@ - ReloopReadyInstance.components.rightChannel.knob3.input + ReloopReadyInstance.components.rightChannel.eqKnob[1].input 0xB1 0x19 @@ -242,7 +320,7 @@ - ReloopReadyInstance.components.rightChannel.filter.input + ReloopReadyInstance.components.rightChannel.eqKnob[0].input 0xB1 0x1A diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 63ded6bc467..1ab5e271f6e 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -13,12 +13,6 @@ class ReloopReady { } constructor() { - /// The controller only offers Low, High and Gain Knobs, - /// settings this to true will remap them to control Low, Mid, High instead. - this.threeBandEQ = false; - - this.backlightButtons = true; - /// On my hardware unit, the tempo faders have such a cheap build-quality /// that the notches in the center are not actually in the middle /// If you find your notches are offset too, do this: @@ -28,10 +22,10 @@ class ReloopReady { /// and read the `rate` value of the channel. If its already 0.5 you should be /// good, if its not that, copy the value here and the fader will be adjusted /// for you. - this.tempoFaderMiddles = [0.5, 0.5]; + this.tempoFaderMiddles = [engine.getSetting("tempoFaderCorrectionLeft"), engine.getSetting("tempoFaderCorrectionRight")]; - components.Button.prototype.off = this.backlightButtons ? this.constructor.singleColorLED.dim : this.constructor.singleColorLED.off; + components.Button.prototype.off = engine.getSetting("useButtonBacklight") ? this.constructor.singleColorLED.dim : this.constructor.singleColorLED.off; components.Button.prototype.on = this.constructor.singleColorLED.lit; @@ -261,43 +255,20 @@ ReloopReady.Channel = class extends components.ComponentContainer { const channel = deckIdx + 1; const group = `[Channel${channel}]`; const eqGroup = `[EqualizerRack1_${group}_Effect1]`; - - if (parent.threeBandEQ) { - this.knob1 = new components.Pot({ - midi: [0xB0 + deckIdx, 0x16], - group: eqGroup, - inKey: "parameter3" - }); - this.knob2 = new components.Pot({ - midi: [0xB0 + deckIdx, 0x17], - group: eqGroup, - inKey: "parameter2" - }); - } else { - this.knob1 = new components.Pot({ - midi: [0xB0 + deckIdx, 0x16], - inKey: "pregain" - }); - - this.knob2 = new components.Pot({ - midi: [0xB0 + deckIdx, 0x17], - group: eqGroup, - inKey: "parameter3" - }); - } - - this.knob3 = new components.Pot({ - midi: [0xB0 + deckIdx, 0x19], - group: eqGroup, - inKey: "parameter1" - }); - - - this.filter = new components.Pot({ - midi: [0xB0 + deckIdx, 0x1A], - group: `[QuickEffectRack1_${group}]`, - inKey: "super1" - }); + const eqLayoutSetting = engine.getSetting("eqLayout"); // bitset string (eg. "10111") indicating which elements are used + const eqKnobAddresses = [0x1A, 0x19, 0x17, 0x16]; + this.eqKnob = [ + [`[QuickEffectRack1_${group}]`, "super1"], + [eqGroup, "parameter1", 0x19], + [eqGroup, "parameter2", 0x17], + [eqGroup, "parameter3", 0x16], + [group, "pregain"]] + .filter((_, i) => eqLayoutSetting[i] === "1") + .map(([group, key], i) => new components.Pot({ + midi: [0xB0 + deckIdx, eqKnobAddresses[i]], + group: group, + inKey: key, + })); this.rate = new components.Pot({ midi: [0xB0 + deckIdx, 0x09], // MSB control: 0x3F @@ -523,7 +494,7 @@ ReloopReady.AutoLoopPadMode = class extends ReloopReady.AbstractLoopPadMode { constructor(deckIdx) { super(deckIdx); - this.currentLoopSizeExp = -2; + this.currentLoopSizeExp = engine.getSetting("defaultLoopRootSize"); this.pads = this.pads.map(control => new components.Button({ midi: [0x94 + deckIdx, control], @@ -663,7 +634,7 @@ ReloopReady.LoopRollPadMode = class extends ReloopReady.AbstractLoopPadMode { constructor(deckIdx) { super(deckIdx); - this.currentLoopSizeExp = -2; + this.currentLoopSizeExp = engine.getSetting("defaultLoopRootSize"); this.pads = this.pads.map(control => new components.Button({ midi: [0x94 + deckIdx, control], @@ -871,7 +842,7 @@ ReloopReady.ScratchBankPadMode = class extends ReloopReady.PadMode { constructor(deckIdx) { super(deckIdx); - this.currentJumpSizeExp = -2; + this.currentJumpSizeExp = engine.getSetting("defaultLoopRootSize"); this.pads = this.pads.map(control => new components.Button({ From 533f74cb6ee5262b824ecb3dedc1fe8d3efe7bb8 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:16:44 +0100 Subject: [PATCH 10/18] refactor: remove dependency on lodash --- res/controllers/Reloop Ready.midi.xml | 1 - res/controllers/Reloop-Ready-scripts.js | 50 ++++++++++++------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/res/controllers/Reloop Ready.midi.xml b/res/controllers/Reloop Ready.midi.xml index 60cb2c53b1c..76d3a9025a5 100644 --- a/res/controllers/Reloop Ready.midi.xml +++ b/res/controllers/Reloop Ready.midi.xml @@ -87,7 +87,6 @@ - diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 1ab5e271f6e..1548e1cc91a 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -170,14 +170,10 @@ class ReloopReady { return (r << 16) | (g << 8) | b; } static dimColor(color) { - return color & 0b00111111; + return color & 0b0011_1111; } - - static arrayRange(start, stop, step = 1){ - return Array.from( - { length: (stop - start) / step + 1 }, - (_value, index) => start + index * step - ); + static clamp(value, min, max) { + return Math.max(min, Math.min(max, value)); } @@ -202,7 +198,7 @@ class ReloopReady { } } -ReloopReady.padColorPalette = { +ReloopReady.padColorPaletteTemplate = { Off: 0x000000, Red: 0xFF0000, Green: 0x00FF00, @@ -224,21 +220,24 @@ ReloopReady.singleColorLED = { ReloopReady.noopInputHandler = (_channel, _control, _value, _status, _group) => {}; (() => { - // rewrite the following without lodash - const fullColorToMidiColorMap = _.keyBy(_.range(0x00, 0x80), ReloopReady.midiToFullColor); + const fullColorToMidiColorMap = {}; + for (let midiColor = 0x00; midiColor <= 0b111_1111; ++midiColor) { + fullColorToMidiColorMap[ReloopReady.midiToFullColor(midiColor)] = midiColor; + } ReloopReady.padColorMapper = new ColorMapper(fullColorToMidiColorMap); // transform padColorPalette to low-res midi approximations: - ReloopReady.padColorPalette = _.mapValues(ReloopReady.padColorPalette, color => { - const litColor = ReloopReady.padColorMapper.getValueForNearestColor(color); + ReloopReady.padColorPalette = {}; + for (const [name, fullColor] of Object.entries(ReloopReady.padColorPaletteTemplate)) { + const litColor = ReloopReady.padColorMapper.getValueForNearestColor(fullColor); const dimColor = ReloopReady.dimColor(litColor); - return { + ReloopReady.padColorPalette[name] = { lit: litColor, dim: dimColor, }; - }); + } })(); components.Button.prototype.sendShifted = true; @@ -259,9 +258,9 @@ ReloopReady.Channel = class extends components.ComponentContainer { const eqKnobAddresses = [0x1A, 0x19, 0x17, 0x16]; this.eqKnob = [ [`[QuickEffectRack1_${group}]`, "super1"], - [eqGroup, "parameter1", 0x19], - [eqGroup, "parameter2", 0x17], - [eqGroup, "parameter3", 0x16], + [eqGroup, "parameter1"], + [eqGroup, "parameter2"], + [eqGroup, "parameter3"], [group, "pregain"]] .filter((_, i) => eqLayoutSetting[i] === "1") .map(([group, key], i) => new components.Pot({ @@ -431,16 +430,15 @@ ReloopReady.HotcuePadMode = class extends ReloopReady.PadMode { ReloopReady.AbstractLoopPadMode = class extends ReloopReady.PadMode { clampLoopSizeExp(loopSizeExp) { - return _.clamp(loopSizeExp, -5, 2); + return ReloopReady.clamp(loopSizeExp, -5, 2); } - setLoopSizeProperties([_size, _pad]) { + setLoopSizeProperties(_size, _pad) { throw new TypeError("AbstractLoopPadMode.setLoopSizeProperties not overwritten!"); } setLoopSizes(loopSizeExp) { - _(_.range(loopSizeExp, loopSizeExp + this.pads.length)) - .map(exp => Math.pow(2, exp)) - .zip(this.pads) - .forEach(this.setLoopSizeProperties); + for (let i = 0; i < this.pads.length; ++i) { + this.setLoopSizeProperties(Math.pow(loopSizeExp + i), this.pads[i]); + } } makeParameterInputHandler(loopSizeChangeAmount) { const theContainer = this; @@ -486,7 +484,7 @@ ReloopReady.AbstractLoopPadMode = class extends ReloopReady.PadMode { }; ReloopReady.AutoLoopPadMode = class extends ReloopReady.AbstractLoopPadMode { - setLoopSizeProperties([size, pad]) { + setLoopSizeProperties(size, pad) { pad.inKey = `beatloop_${size}_toggle`; pad.outKey = `beatloop_${size}_enabled`; } @@ -615,7 +613,7 @@ ReloopReady.SamplerPadMode = class extends ReloopReady.PadMode { } }; ReloopReady.LoopRollPadMode = class extends ReloopReady.AbstractLoopPadMode { - setLoopSizeProperties([size, pad]) { + setLoopSizeProperties(size, pad) { pad.inKey = `beatlooproll_${size}_activate`; pad.outKey = `beatloop_${size}_enabled`; } @@ -813,7 +811,7 @@ ReloopReady.Pitch = class extends ReloopReady.PadMode { // PadMode for beatjumping. ReloopReady.ScratchBankPadMode = class extends ReloopReady.PadMode { clampJumpSizeExp(jumpSizeExp) { - return _.clamp(jumpSizeExp, -5, 6); + return ReloopReady.clamp(jumpSizeExp, -5, 6); } setjumpSizeExp(jumpSizeExp) { const middle = this.pads.length / 2; From 3ded2bf29c027b62e2ffef827134f5d24a663722 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Tue, 31 Dec 2024 15:55:11 +0100 Subject: [PATCH 11/18] polish: ensure that padmode parameter buttons work in all modes --- res/controllers/Reloop-Ready-scripts.js | 55 +++++++------------------ 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 1548e1cc91a..432e7a92a93 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -584,6 +584,7 @@ ReloopReady.ManualLoopPadMode = class extends ReloopReady.PadMode { input: ReloopReady.makeButtonDownInputHandler(function() { loopMoveBeats *= 0.5; }), + trigger: ReloopReady.makeIsPressedTrigger(), }); this.parameterRight = new components.Button({ midi: [0x94 + deckIdx, 0x29], @@ -591,6 +592,7 @@ ReloopReady.ManualLoopPadMode = class extends ReloopReady.PadMode { input: ReloopReady.makeButtonDownInputHandler(function() { loopMoveBeats *= 2; }), + trigger: ReloopReady.makeIsPressedTrigger(), }); this.reconnectComponents(c => { @@ -643,17 +645,6 @@ ReloopReady.LoopRollPadMode = class extends ReloopReady.AbstractLoopPadMode { outConnect: false, })); - this.parameterLeft = new components.Button({ - midi: [0x94 + deckIdx, 0x28], //shifted control: 0x2A - shiftOffset: 0x2, - input: this.makeParameterInputHandler(-1), - }); - this.parameterRight = new components.Button({ - midi: [0x94 + deckIdx, 0x29], //shifted control: 0x2B - shiftOffset: 0x2, - input: this.makeParameterInputHandler(1), - }); - this.setLoopSizes(this.currentLoopSizeExp); } }; @@ -776,6 +767,10 @@ ReloopReady.Pitch = class extends ReloopReady.PadMode { this.pads[n] = new this.PerformancePad(n); } + const litTrigger = function() { + this.send(this.off); + }; + this.parameterLeft = new components.Button({ midi: [0x94 + deckIdx, 0x28], shiftOffset: 0x2, @@ -789,6 +784,7 @@ ReloopReady.Pitch = class extends ReloopReady.PadMode { } theContainer.pads.forEachComponent(c => c.trigger()); }), + trigger: litTrigger, }); this.parameterRight = new components.Button({ midi: [0x94 + deckIdx, 0x29], @@ -803,17 +799,19 @@ ReloopReady.Pitch = class extends ReloopReady.PadMode { } theContainer.pads.forEachComponent(c => c.trigger()); }), + trigger: litTrigger, }); } }; -// There is no such thing as a scratch bank in Mixxx so I'm repurpusing this +// There is no such thing as a scratch bank in Mixxx so I'm repurposing this // PadMode for beatjumping. -ReloopReady.ScratchBankPadMode = class extends ReloopReady.PadMode { - clampJumpSizeExp(jumpSizeExp) { +ReloopReady.ScratchBankPadMode = class extends ReloopReady.AbstractLoopPadMode { + // Note that "Jump" and "Loop" is used interchangeably used here + clampLoopSizeExp(jumpSizeExp) { return ReloopReady.clamp(jumpSizeExp, -5, 6); } - setjumpSizeExp(jumpSizeExp) { + setLoopSizes(jumpSizeExp) { const middle = this.pads.length / 2; let jumpsize = Math.pow(2, jumpSizeExp); for (let i = 0; i < middle; ++i) { @@ -826,21 +824,10 @@ ReloopReady.ScratchBankPadMode = class extends ReloopReady.PadMode { jumpsize *= 2; } } - makeParameterInputHandler(jumpSizeChangeAmount) { - const theContainer = this; - return ReloopReady.makeButtonDownInputHandler(function() { - const newJumpSize = theContainer.clampJumpSizeExp(theContainer.currentJumpSizeExp + jumpSizeChangeAmount); - if (newJumpSize !== theContainer.currentJumpSizeExp) { - theContainer.currentJumpSizeExp = newJumpSize; - theContainer.setjumpSizeExp(newJumpSize); - theContainer.reconnectComponents(); - } - }); - } constructor(deckIdx) { super(deckIdx); - this.currentJumpSizeExp = engine.getSetting("defaultLoopRootSize"); + this.currentLoopSizeExp = engine.getSetting("defaultLoopRootSize"); this.pads = this.pads.map(control => new components.Button({ @@ -852,19 +839,7 @@ ReloopReady.ScratchBankPadMode = class extends ReloopReady.PadMode { outConnect: false, }) ); - - this.parameterLeft = new components.Button({ - midi: [0x94 + deckIdx, 0x28], - shiftOffset: 0x2, - input: this.makeParameterInputHandler(-1), - }); - this.parameterRight = new components.Button({ - midi: [0x94 + deckIdx, 0x29], - shiftOffset: 0x2, - input: this.makeParameterInputHandler(1), - }); - - this.setjumpSizeExp(this.currentJumpSizeExp); + this.setLoopSizes(this.currentLoopSizeExp); } }; ReloopReady.BeatGridPadMode = class extends ReloopReady.PadMode { From 716285cab0b1ead3280e141d5de15f5bb993b0e4 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Tue, 31 Dec 2024 16:20:50 +0100 Subject: [PATCH 12/18] fix: shift+loadbutton for eject input and led feedback --- res/controllers/Reloop Ready.midi.xml | 16 ++++----- res/controllers/Reloop-Ready-scripts.js | 44 +++++++++++++++---------- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/res/controllers/Reloop Ready.midi.xml b/res/controllers/Reloop Ready.midi.xml index 76d3a9025a5..75465c08b0f 100644 --- a/res/controllers/Reloop Ready.midi.xml +++ b/res/controllers/Reloop Ready.midi.xml @@ -234,7 +234,7 @@ - ReloopReadyInstance.components.leftChannel.load.input + ReloopReadyInstance.components.loadLeft.input 0x9E 0x02 @@ -243,7 +243,7 @@ - ReloopReadyInstance.components.leftChannel.load.input + ReloopReadyInstance.components.loadLeft.input 0x8E 0x02 @@ -253,7 +253,7 @@ - ReloopReadyInstance.components.leftChannel.load.input + ReloopReadyInstance.components.loadLeft.input 0x9E 0x1B @@ -262,7 +262,7 @@ - ReloopReadyInstance.components.leftChannel.load.input + ReloopReadyInstance.components.loadLeft.input 0x8E 0x1B @@ -356,7 +356,7 @@ - ReloopReadyInstance.components.rightChannel.load.input + ReloopReadyInstance.components.loadRight.input 0x9E 0x03 @@ -365,7 +365,7 @@ - ReloopReadyInstance.components.rightChannel.load.input + ReloopReadyInstance.components.loadRight.input 0x8E 0x03 @@ -375,7 +375,7 @@ - ReloopReadyInstance.components.rightChannel.load.input + ReloopReadyInstance.components.loadRight.input 0x9E 0x10 @@ -384,7 +384,7 @@ - ReloopReadyInstance.components.rightChannel.load.input + ReloopReadyInstance.components.loadRight.input 0x8E 0x10 diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 432e7a92a93..89c8394c9ee 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -92,6 +92,33 @@ class ReloopReady { }) }); + this.components.loadLeft = new components.Button({ + midi: [0x9E, 0x02], + shiftOffset: 0x19, + group: "[Channel1]", + shift: function() { + this.inKey = "eject"; + this.outKey = this.inKey; + }, + unshift: function() { + this.inKey = "LoadSelectedTrack"; + this.outKey = this.inKey; + } + }); + this.components.loadRight = new components.Button({ + midi: [0x9E, 0x03], + shiftOffset: 0x0D, + group: "[Channel2]", + shift: function() { + this.inKey = "eject"; + this.outKey = this.inKey; + }, + unshift: function() { + this.inKey = "LoadSelectedTrack"; + this.outKey = this.inKey; + } + }); + const shiftableComponents = this.components; this.shift = new components.Button({ @@ -305,23 +332,6 @@ ReloopReady.Channel = class extends components.ComponentContainer { type: components.Button.prototype.types.toggle, }); - // TODO properly handle output messages here - // the constant offset between channels doesn't seem to apply - // left shift: 0x1B, right shift: 0x10 - this.load = new components.Button({ - midi: [0x9E, 0x02 + deckIdx], - shiftOffset: 0x0D, - shift: function() { - this.inKey = "eject"; - this.outKey = this.inKey; - }, - unshift: function() { - this.inKey = "LoadSelectedTrack"; - this.outKey = this.inKey; - } - }); - - this.reconnectComponents(c => { if (c.group === undefined) { c.group = group; From a762d774be05259d388f8824aa05eddbced8d322 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 1 Jan 2025 13:59:35 +0100 Subject: [PATCH 13/18] fix: add correct no-unused-vars eslint exception --- res/controllers/Reloop-Ready-scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 89c8394c9ee..f0cd3d6299e 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -1,5 +1,5 @@ -// eslint-disable-next-line no-var, no-unused-vars +// eslint-disable-next-line no-var, @typescript-eslint/no-unused-vars var ReloopReadyInstance = { init: function() { ReloopReady.init(); From d046fc1db8f6dca18bb81b3c55d1244830fe40e0 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 1 Jan 2025 14:14:28 +0100 Subject: [PATCH 14/18] chore: avoid partially unsupported _ in number literals --- res/controllers/Reloop-Ready-scripts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index f0cd3d6299e..e1760ff16de 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -197,7 +197,7 @@ class ReloopReady { return (r << 16) | (g << 8) | b; } static dimColor(color) { - return color & 0b0011_1111; + return color & 0b00111111; } static clamp(value, min, max) { return Math.max(min, Math.min(max, value)); @@ -248,7 +248,7 @@ ReloopReady.noopInputHandler = (_channel, _control, _value, _status, _group) => (() => { const fullColorToMidiColorMap = {}; - for (let midiColor = 0x00; midiColor <= 0b111_1111; ++midiColor) { + for (let midiColor = 0x00; midiColor <= 0b1111111; ++midiColor) { fullColorToMidiColorMap[ReloopReady.midiToFullColor(midiColor)] = midiColor; } From ad9338ae4f33a3784e6969741b514cae9862d40f Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Wed, 1 Jan 2025 14:42:23 +0100 Subject: [PATCH 15/18] remove outdated TODOs; add manual link; remove duplicate script-file inclusions --- res/controllers/Reloop Ready.midi.xml | 4 +--- res/controllers/Reloop-Ready-scripts.js | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/res/controllers/Reloop Ready.midi.xml b/res/controllers/Reloop Ready.midi.xml index 75465c08b0f..b77ae49e7ba 100644 --- a/res/controllers/Reloop Ready.midi.xml +++ b/res/controllers/Reloop Ready.midi.xml @@ -4,8 +4,7 @@ Reloop Ready Swiftb0y feature-complete mapping of the Reloop Ready hardware. Development kindly sponsored by Samgarr - - + reloop_ready + + + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.parameterRight.input + 0x95 + 0x2B + + + + + + + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.parameterRight.input + 0x85 + 0x2B + + + + + + + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.parameterLeft.input + 0x95 + 0x2A + + + + + + + ReloopReadyInstance.components.rightDeck.padUnit.currentLayer.parameterLeft.input + 0x85 + 0x2A + + + + @@ -1988,6 +2024,42 @@ + + + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.parameterRight.input + 0x94 + 0x2B + + + + + + + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.parameterRight.input + 0x84 + 0x2B + + + + + + + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.parameterLeft.input + 0x94 + 0x2A + + + + + + + ReloopReadyInstance.components.leftDeck.padUnit.currentLayer.parameterLeft.input + 0x84 + 0x2A + + + + ReloopReadyInstance.components.leftDeck.padUnit.modeButton.input diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 147f7bdf413..eb792e64de7 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -435,6 +435,42 @@ ReloopReady.HotcuePadMode = class extends ReloopReady.PadMode { colorMapper: ReloopReady.padColorMapper, outConnect: false, })); + this.parameterLeft = new components.Button({ + midi: [0x94 + deckIdx, 0x28], //shifted control: 0x2A + shiftOffset: 0x2, + shift: function() { + this.inKey = "hotcue_focus_color_prev"; + this.outKey = this.inKey; + this.trigger = function() { + this.send(this.off); + }; + }, + unshift: function() { + this.inKey = undefined; // no function ATM + this.outKey = this.inKey; + this.trigger = function() { + this.send(ReloopReady.singleColorLED.off); + }; + } + }); + this.parameterRight = new components.Button({ + midi: [0x94 + deckIdx, 0x29], //shifted control: 0x2B + shiftOffset: 0x2, + shift: function() { + this.inKey = "hotcue_focus_color_next"; + this.outKey = this.inKey; + this.trigger = function() { + this.send(this.off); + }; + }, + unshift: function() { + this.inKey = undefined; // no function ATM + this.outKey = this.inKey; + this.trigger = function() { + this.send(ReloopReady.singleColorLED.off); + }; + } + }); } }; From 5320d020071f6b161cf2e85a07aa2edbd040b9e8 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Fri, 3 Jan 2025 15:55:05 +0100 Subject: [PATCH 17/18] fix: disable softtakeover for crossfader This avoids an issue where it could get detached when moved quickly. This can happen often on this controller due to the cheap built quality of the faders, resulting in low-resolution data that is larger than the softtakeover treshold. --- res/controllers/Reloop-Ready-scripts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index eb792e64de7..960a5447bec 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -45,7 +45,8 @@ class ReloopReady { this.components.crossfader = new components.Pot({ midi: [0xBE, 0x08], group: "[Master]", - inKey: "crossfader" + inKey: "crossfader", + softTakeover: false, // see volume faders }); this.components.masterVol = new components.Pot({ From d7ef023ab93c4b6c75227e0224153711524f22b4 Mon Sep 17 00:00:00 2001 From: Swiftb0y <12380386+Swiftb0y@users.noreply.github.com> Date: Sat, 4 Jan 2025 20:17:59 +0100 Subject: [PATCH 18/18] refactor: use util methods for vinyl button --- res/controllers/Reloop-Ready-scripts.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js index 960a5447bec..80cf0573526 100644 --- a/res/controllers/Reloop-Ready-scripts.js +++ b/res/controllers/Reloop-Ready-scripts.js @@ -213,10 +213,10 @@ class ReloopReady { static makeButtonDownInputHandler(handler) { return function(channel, control, value, status, _group) { this.isPressed = this.isPress(channel, control, value, status); - this.trigger(); if (this.isPressed) { handler.call(this, value); } + this.trigger(); }; } static makeIsPressedTrigger() { @@ -1092,12 +1092,9 @@ ReloopReady.Deck = class extends components.Deck { // prevent binding to slip_enabled when unshifted // (otherwise vinyl led would glitch when a new track is loaded). this.inKey = this.outKey = undefined; - this.input = function(channel, control, value, status, _group) { - if (this.isPress(channel, control, value, status)) { - thisDeck.jog.vinylMode = !thisDeck.jog.vinylMode; - this.output(thisDeck.jog.vinylMode); - } - }; + this.input = ReloopReady.makeButtonDownInputHandler(function() { + thisDeck.jog.vinylMode = !thisDeck.jog.vinylMode; + }); }, trigger: function() { this.output(thisDeck.jog.vinylMode);