diff --git a/res/controllers/Reloop Ready.midi.xml b/res/controllers/Reloop Ready.midi.xml new file mode 100644 index 000000000000..bb73893bc490 --- /dev/null +++ b/res/controllers/Reloop Ready.midi.xml @@ -0,0 +1,2145 @@ + + + + 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 + + + + + + + + + + + + + + + + + diff --git a/res/controllers/Reloop-Ready-scripts.js b/res/controllers/Reloop-Ready-scripts.js new file mode 100644 index 000000000000..d3d5e3496e4d --- /dev/null +++ b/res/controllers/Reloop-Ready-scripts.js @@ -0,0 +1,1133 @@ +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 = function(color) { + + var b = (color & 3) << 6; // ES3 does not support binary literals + var g = (color >> 2 & 3) << 6; + var r = (color >> 4 & 3) << 6; + var i = color >> 5 & 1; + + if (i === 0) { + // half the RGB intensity + r >> 1; + g >> 1; + b >> 1; + } + 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.dimColor = function(color) { + return color & 0x3F; // dim color by stripping the "intensity bit" +}; + +// IIFE for not leaking private variables +(function() { + + var 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, function(color) { + var litColor = ReloopReady.padColorMapper.getValueForNearestColor(color); + var 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 + * @param {(value: number) => void} func callback that is called on ButtonDown + */ +ReloopReady.makeButtonDownInputHandler = function(func) { + return function(channel, control, value, status, _group) { + var isPress = this.isPress(channel, control, value, status); + this.output(isPress); + if (!isPress) { + return; + } + func.call(this, value); + }; +}; + +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({ + group: "[Master]", + inKey: "crossfader" + }); + + this.components.masterVol = new components.Pot({ + group: "[Master]", + inKey: "gain" + }); + + this.components.cueMix = new components.Pot({ + group: "[Master]", + inKey: "headMix" + }); + + this.components.cueVol = new components.Pot({ + group: "[Master]", + inKey: "headGain" + }); + + this.components.browse = new components.ComponentContainer({ + button: new components.Button({ + 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({ + group: "[Library]", + unshift: function() { + this.inKey = "MoveVertical"; + }, + shift: function() { + this.inKey = "MoveFocus"; + } + }) + }); + + var shiftableComponents = this.components; + + this.shift = new components.Button({ + input: function(channel, control, value, status, _group) { + shiftableComponents.forEachComponent(function(comp) { + comp.disconnect(); + }); + if (this.isPress(channel, control, value, status)) { + shiftableComponents.shift(); + } else { + shiftableComponents.unshift(); + } + shiftableComponents.forEachComponent(function(comp) { + comp.connect(); + comp.trigger(); + }); + }, + }); + + // request controls status per standard serato sysex + midi.sendSysexMsg([0xF0, 0x00, 0x20, 0x7F, 0x00, 0xF7]); +}; + +ReloopReady.shutdown = function() { + this.components.shutdown(); +}; + + +ReloopReady.Channel = function(index) { + var channel = index + 1; + var group = "[Channel" + channel + "]"; + var eqGroup = "[EqualizerRack1_[Channel" + channel + "]_Effect1]"; + + if (ReloopReady.threeBandEQ) { + this.knob1 = new components.Pot({ + group: eqGroup, + inKey: "parameter3" + }); + this.knob2 = new components.Pot({ + group: eqGroup, + inKey: "parameter2" + }); + } else { + this.knob1 = new components.Pot({ + inKey: "pregain" + }); + + this.knob2 = new components.Pot({ + group: eqGroup, + inKey: "parameter3" + }); + } + + this.knob3 = new components.Pot({ + group: eqGroup, + inKey: "parameter1" + }); + + + this.filter = new components.Pot({ + group: "[QuickEffectRack1_[Channel" + channel + "]]", + inKey: "super1" + }); + + this.rate = new components.Pot({ + inKey: "rate", + max: 1023, + // see explanation of ReloopReady.tempoFaderMiddles for info + inValueScale: function(value) { + var 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({ + 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(function(c) { + if (c.group === undefined) { + c.group = group; + } + }); +}; + +ReloopReady.Channel.prototype = new components.ComponentContainer(); + +ReloopReady.FxUnit = function(index) { + + var midiOn = 0x98 + index; + var channel = index + 1; + + this.level = new components.Pot({ + group: "[EffectRack1_EffectUnit" + channel + "]", + inKey: "super1", + }); + + this.fxButtons = _.map(_.range(0x00, 0x03), function(i) { + return new components.Button({ + midi: [midiOn, i], + shiftOffset: 0xA, + group: "[EffectRack1_EffectUnit" + channel + "_Effect" + (i + 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({ + 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({ + 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) { + var theContainer = this; + components.ComponentContainer.call(this, obj); + // interpret this an interface definition + this.pads = Array(8); + this.parameterLeft = new components.Button(); + this.parameterRight = new components.Button(); + this.shutdown = function() { + // we want the pads to be completely off when shutdown, not dimmed + _.forEach(function(pad) { + pad.send(0x00); + }, theContainer.pads); + theContainer.parameterLeft.shutdown(); + theContainer.parameterRight.shutdown(); + }; +}; + +ReloopReady.PadMode.prototype = Object.create(components.ComponentContainer.prototype); + +ReloopReady.HotcuePadMode = function(index) { + ReloopReady.PadMode.call(this); + + this.pads = _.map(this.pads, function(_pads, i) { + return new components.HotcueButton({ + midi: [0x94 + index, 0x14 + i], + 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; + var theContainer = this; + + var clampLoopSizeExp = function(loopSizeExp) { + return _.clamp(loopSizeExp, -5, -2); + }; + + this.setLoopSizes = _.bind(function(loopSizeExp) { + _(_.range(loopSizeExp, loopSizeExp + 8)) + .map(function(exp) { return Math.pow(2, exp); }) + .zip(this.pads) + .forEach(function(arr) { + var size = arr[0]; + var pad = arr[1]; + pad.inKey = "beatloop_" + size + "_toggle"; + pad.outKey = "beatloop_" + size + "_enabled"; + }); + }, this); + + for (var 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, + }); + } + + var makeParameterInputHandler = function(loopSizeChangeAmount) { + return function(channel, control, value, status, _group) { + var pressed = this.isPress(channel, control, value, status); + if (pressed) { + var 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], + shiftOffset: 0x2, + input: makeParameterInputHandler(-1), + }); + this.parameterRight = new components.Button({ + midi: [0x94 + index, 0x29], + 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); + + var loopMoveBeats = 4; + var 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(); + }, + }); + + _.forEach(this.pads, function(c, i) { + c.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(function(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 = _.map(this.pads, function(_pads, i) { + return 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; + var theContainer = this; + + var clampLoopSizeExp = function(loopSizeExp) { + return _.clamp(loopSizeExp, -5, -2); + }; + + this.setLoopSizes = _.bind(function(loopSizeExp) { + _(_.range(loopSizeExp, loopSizeExp + 8)) + .map(function(exp) { return Math.pow(2, exp); }) + .zip(this.pads) + .forEach(function(arr) { + var size = arr[0]; + var pad = arr[1]; + pad.inKey = "beatlooproll_" + size + "_activate"; + pad.outKey = "beatloop_" + size + "_enabled"; + }); + }, this); + + for (var 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, + }); + } + + var makeParameterInputHandler = function(loopSizeChangeAmount) { + return ReloopReady.makeButtonDownInputHandler(function() { + var 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); + + var PitchPlayRange = { + UP: 0, + MID: 1, + DOWN: 2, + }; + + //this.ledControl = DJ505.PadMode.SAMPLER; + var color = ReloopReady.padColorPalette.Purple; + var cuepoint = 1; + var range = PitchPlayRange.MID; + var 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.ColorMapper; + 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) + var 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) { + var 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) { + var 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); + } + })(); + 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) { + var 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")) { + var 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 (var 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(function(component) { + component.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(function(component) { + component.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; + var theContainer = this; + + var clampJumpSizeExp = function(jumpSizeExp) { + return _.clamp(jumpSizeExp, -5, 6); + }; + + this.setjumpSizeExp = _.bind(function(jumpSizeExp) { + var middle = this.pads.length / 2; + var jumpsize = Math.pow(2, jumpSizeExp); + for (var i = 0; i < middle; ++i) { + var padTop = this.pads[i]; + padTop.inKey = "beatjump_" + jumpsize + "_forward"; + padTop.outKey = padTop.inKey; + var padBot = this.pads[i + middle]; + padBot.inKey = "beatjump_" + jumpsize + "_backward"; + padBot.outKey = padTop.inKey; + jumpsize *= 2; + } + }, this); + + + for (var 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, + }); + } + + var makeParameterInputHandler = function(jumpSizeChangeAmount) { + return ReloopReady.makeButtonDownInputHandler(function() { + var 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); + + var 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_translate_faster", + on: ReloopReady.padColorPalette.Red.lit, + off: ReloopReady.padColorPalette.Red.dim, + trigger: function() { + this.output(); + }, + }); + this.pads[3] = new components.Button({ + key: "beats_translate_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, + }); + + _.forEach(this.pads, function(c, i) { + c.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(function(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 + var padModeInstances = _.map(ReloopReady.controlPadModeAssoc, function(obj) { + var modeInstance = new (obj.mode)(index); + // make sure no multiple components have "ownership" over each pad + modeInstance.forEachComponent(function(c) { + c.disconnect(); + }); + return { + pos: obj.pos, + control: obj.control, + modeInstance: modeInstance, + }; + }); + + this.currentLayer = padModeInstances[0].modeInstance; + this.currentLayer.reconnectComponents(); + + var thisContainer = this; + + var applyLayer = function(layer) { + if (thisContainer.currentLayer === layer) { + return; + } + + thisContainer.currentLayer.forEachComponent(function(component) { + component.disconnect(); + }); + + if (thisContainer.isShifted) { + layer.shift(); + } else { + layer.unshift(); + } + + layer.forEachComponent(function(component) { + component.connect(); + component.trigger(); + }); + + thisContainer.currentLayer = layer; + + _.forEach(thisContainer.padModeSelectors, function(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 + var makePadModeInputHandler = function(layer) { + return (layer === undefined) ? + function(_channel, _control, _value, _status, _group) {} : + function(channel, control, value, status, _group) { + if (!this.isPress(channel, control, value, status)) { + return; + } + applyLayer(layer); + }; + }; + + + // create physical buttons for changing the layers + this.padModeSelectors = Array(8); + _.forEach(padModeInstances, function(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 = _.map(this.padModeSelectors, function(obj) { + // for mixxx 2.4: + // return obj ?? new components.Button({}); + return (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], + input: function(_channel, _control, _value, _status, _group) { + } + }); +}; + +ReloopReady.PadContainer.prototype = Object.create(components.ComponentContainer.prototype); + +ReloopReady.Deck = function(index) { + + var channel = index + 1; + + components.Deck.call(this, channel); + + var thisDeck = this; + var 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({ + alpha: 1/8, + deck: channel, + wheelResolution: 300, + }); + + + this.keylock = new components.Button({ + midi: [midiOn, 0x0D], + shiftOffset: 0x1C, + type: components.Button.prototype.types.toggle, + shift: function() { + this.inKey = "sync_key"; + this.outKey = this.inKey; + }, + unshift: function() { + 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();