diff --git a/res/controllers/Numark Mixtrack Platinum FX.midi.xml b/res/controllers/Numark Mixtrack Platinum FX.midi.xml new file mode 100644 index 00000000000..92be36f2cd0 --- /dev/null +++ b/res/controllers/Numark Mixtrack Platinum FX.midi.xml @@ -0,0 +1,3875 @@ + + + + Numark Mixtrack Platinum FX + Evoixmr mixxx 2.4 version updated from - QGazQ tweaks of Octopussy based on mapping from h67ma, bad1dea5, photoenix, Matthew Nicholson, Ending, Kaj Bostrom + Mapping for the Numark Mixtrack Platinum FX. + https://mixxx.discourse.group/t/numark-mixtrack-platinum-fx-mapping/19985 + + + + + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].playButton.input + 0x80 + 0x00 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].playButton.input + 0x81 + 0x00 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].playButton.input + 0x82 + 0x00 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].playButton.input + 0x83 + 0x00 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x84 + 0x00 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x85 + 0x00 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x86 + 0x00 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x87 + 0x00 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].effect1 + 0x88 + 0x00 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].playButton.input + 0x90 + 0x00 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].playButton.input + 0x91 + 0x00 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].playButton.input + 0x92 + 0x00 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].playButton.input + 0x93 + 0x00 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x94 + 0x00 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x95 + 0x00 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x96 + 0x00 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x97 + 0x00 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].effect1 + 0x98 + 0x00 + + + + + + [Library] + MoveVertical + MIDI Learned from 1 messages. + 0xBF + 0x00 + + + + + + [Library] + ScrollVertical + MIDI Learned from 2 messages. + 0xBF + 0x01 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].cueButton.input + 0x80 + 0x01 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].cueButton.input + 0x81 + 0x01 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].cueButton.input + 0x82 + 0x01 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].cueButton.input + 0x83 + 0x01 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].effect2 + 0x88 + 0x01 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].cueButton.input + 0x90 + 0x01 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].cueButton.input + 0x91 + 0x01 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].cueButton.input + 0x92 + 0x01 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[3].cueButton.input + 0x93 + 0x01 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].effect2 + 0x98 + 0x01 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].syncButton.input + 0x80 + 0x02 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].syncButton.input + 0x81 + 0x02 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].syncButton.input + 0x82 + 0x02 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].syncButton.input + 0x83 + 0x02 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x84 + 0x02 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x85 + 0x02 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x86 + 0x02 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x87 + 0x02 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].effect3 + 0x88 + 0x02 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loadButton.input + 0x8F + 0x02 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].syncButton.input + 0x90 + 0x02 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].syncButton.input + 0x91 + 0x02 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].syncButton.input + 0x92 + 0x02 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].syncButton.input + 0x93 + 0x02 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x94 + 0x02 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x95 + 0x02 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x96 + 0x02 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x97 + 0x02 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].effect3 + 0x98 + 0x02 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loadButton.input + 0x9F + 0x02 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].syncButton.input + 0x80 + 0x03 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].syncButton.input + 0x81 + 0x03 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].syncButton.input + 0x82 + 0x03 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].syncButton.input + 0x83 + 0x03 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].effect1 + 0x89 + 0x03 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loadButton.input + 0x8F + 0x03 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].syncButton.input + 0x90 + 0x03 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].syncButton.input + 0x91 + 0x03 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].syncButton.input + 0x92 + 0x03 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].syncButton.input + 0x93 + 0x03 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].effect1 + 0x99 + 0x03 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loadButton.input + 0x9F + 0x03 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].enableSwitch + 0xB8 + 0x03 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].enableSwitch + 0xB9 + 0x03 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].playButtonbeatgrid + 0x80 + 0x04 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].playButtonbeatgrid + 0x81 + 0x04 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].playButtonbeatgrid + 0x82 + 0x04 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].playButtonbeatgrid + 0x83 + 0x04 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].effect2 + 0x89 + 0x04 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loadButton.input + 0x8F + 0x04 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].playButtonbeatgrid + 0x90 + 0x04 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].playButtonbeatgrid + 0x91 + 0x04 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].playButtonbeatgrid + 0x92 + 0x04 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].playButtonbeatgrid + 0x93 + 0x04 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].effect2 + 0x99 + 0x04 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loadButton.input + 0x9F + 0x04 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].dryWetKnob.input + 0xB8 + 0x04 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].dryWetKnob.input + 0xB8 + 0x04 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].cueButton.input + 0x80 + 0x05 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].cueButton.input + 0x81 + 0x05 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].cueButton.input + 0x82 + 0x05 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].cueButton.input + 0x83 + 0x05 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].effect3 + 0x89 + 0x05 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loadButton.input + 0x8F + 0x05 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].cueButton.input + 0x90 + 0x05 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].cueButton.input + 0x91 + 0x05 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].cueButton.input + 0x92 + 0x05 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].cueButton.input + 0x93 + 0x05 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].effect3 + 0x99 + 0x05 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loadButton.input + 0x9F + 0x05 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].effectParam.input + 0xB8 + 0x05 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].effectParam.input + 0xB8 + 0x05 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].effectParam2.input + 0xB8 + 0x05 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].effectParam2.input + 0xB8 + 0x05 + + + + + + [EffectRack1_EffectUnit1] + MixtrackPlatinumFX.effect[0].effectParam3.input + 0xB8 + 0x05 + + + + + + [EffectRack1_EffectUnit2] + MixtrackPlatinumFX.effect[1].effectParam3.input + 0xB8 + 0x05 + + + + + + [Library] + MixtrackPlatinumFX.browse.knobButton.input + 0x8F + 0x06 + + + + + + [Channel1] + MixtrackPlatinumFX.wheelTouch + 0x90 + 0x06 + + + + + + [Channel2] + MixtrackPlatinumFX.wheelTouch + 0x91 + 0x06 + + + + + + [Channel3] + MixtrackPlatinumFX.wheelTouch + 0x92 + 0x06 + + + + + + [Channel4] + MixtrackPlatinumFX.wheelTouch + 0x93 + 0x06 + + + + + + [Library] + MixtrackPlatinumFX.browse.knobButton.input + 0x9F + 0x06 + + + + + + [Channel1] + MixtrackPlatinumFX.wheelTurn + 0xB0 + 0x06 + + + + + + [Channel2] + MixtrackPlatinumFX.wheelTurn + 0xB1 + 0x06 + + + + + + [Channel3] + MixtrackPlatinumFX.wheelTurn + 0xB2 + 0x06 + + + + + + [Channel4] + MixtrackPlatinumFX.wheelTurn + 0xB3 + 0x06 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].scratchToggle.input + 0x80 + 0x07 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].scratchToggle.input + 0x81 + 0x07 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].scratchToggle.input + 0x82 + 0x07 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].scratchToggle.input + 0x83 + 0x07 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x84 + 0x07 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x85 + 0x07 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x86 + 0x07 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x87 + 0x07 + + + + + + [Library] + MixtrackPlatinumFX.browse.knobButton.input + 0x8F + 0x07 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].scratchToggle.input + 0x90 + 0x07 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].scratchToggle.input + 0x91 + 0x07 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].scratchToggle.input + 0x92 + 0x07 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].scratchToggle.input + 0x93 + 0x07 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x94 + 0x07 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x95 + 0x07 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x96 + 0x07 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x97 + 0x07 + + + + + + [Library] + MixtrackPlatinumFX.browse.knobButton.input + 0x9F + 0x07 + + + + + + [Channel1] + MixtrackPlatinumFX.deckSwitch + 0x80 + 0x08 + + + + + + [Channel2] + MixtrackPlatinumFX.deckSwitch + 0x81 + 0x08 + + + + + + [Channel3] + MixtrackPlatinumFX.deckSwitch + 0x82 + 0x08 + + + + + + [Channel4] + MixtrackPlatinumFX.deckSwitch + 0x83 + 0x08 + + + + + + [Channel1] + MixtrackPlatinumFX.deckSwitch + 0x90 + 0x08 + + + + + + [Channel2] + MixtrackPlatinumFX.deckSwitch + 0x91 + 0x08 + + + + + + [Channel3] + MixtrackPlatinumFX.deckSwitch + 0x92 + 0x08 + + + + + + [Channel4] + MixtrackPlatinumFX.deckSwitch + 0x93 + 0x08 + + + + + + [Master] + crossfader + 0xB1 + 0x08 + + + + + + [Master] + crossfader + 0xBF + 0x08 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].tap.input + 0x88 + 0x09 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].tap.input + 0x89 + 0x09 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].tap.input + 0x98 + 0x09 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].tap.input + 0x99 + 0x09 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitch.inputMSB + 0xB0 + 0x09 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitch.inputMSB + 0xB1 + 0x09 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitch.inputMSB + 0xB2 + 0x09 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitch.inputMSB + 0xB3 + 0x09 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitchBendUp.input + 0x80 + 0x0B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitchBendUp.input + 0x81 + 0x0B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitchBendUp.input + 0x82 + 0x0B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitchBendUp.input + 0x83 + 0x0B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x84 + 0x0B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x85 + 0x0B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x86 + 0x0B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x87 + 0x0B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitchBendUp.input + 0x90 + 0x0B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitchBendUp.input + 0x91 + 0x0B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitchBendUp.input + 0x92 + 0x0B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitchBendUp.input + 0x93 + 0x0B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x94 + 0x0B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x95 + 0x0B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x96 + 0x0B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x97 + 0x0B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitchBendDown.input + 0x80 + 0x0C + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitchBendDown.input + 0x81 + 0x0C + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitchBendDown.input + 0x82 + 0x0C + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitchBendDown.input + 0x83 + 0x0C + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitchBendDown.input + 0x90 + 0x0C + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitchBendDown.input + 0x91 + 0x0C + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitchBendDown.input + 0x92 + 0x0C + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitchBendDown.input + 0x93 + 0x0C + + + + + + [Master] + MixtrackPlatinumFX.gains.cueGain.input + 0xBF + 0x0C + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x84 + 0x0D + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x85 + 0x0D + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x86 + 0x0D + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x87 + 0x0D + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x94 + 0x0D + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x95 + 0x0D + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x96 + 0x0D + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x97 + 0x0D + + + + + + [Master] + MixtrackPlatinumFX.gains.cueMix.input + 0xBF + 0x0D + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x84 + 0x0F + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x85 + 0x0F + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x86 + 0x0F + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x87 + 0x0F + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.modeButtonPress + 0x94 + 0x0F + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.modeButtonPress + 0x95 + 0x0F + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.modeButtonPress + 0x96 + 0x0F + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.modeButtonPress + 0x97 + 0x0F + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x14 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x14 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x14 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x14 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x14 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x14 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x14 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x14 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x15 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x15 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x15 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x15 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x15 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x15 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x15 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x15 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x16 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x16 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x16 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x16 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x16 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x16 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x16 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x16 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].gain.input + 0xB0 + 0x16 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].gain.input + 0xB1 + 0x16 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].gain.input + 0xB2 + 0x16 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].gain.input + 0xB3 + 0x16 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x17 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x17 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x17 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x17 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x17 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x17 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x17 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x17 + + + + + + [EqualizerRack1_[Channel1]_Effect1] + MixtrackPlatinumFX.deck[0].treble.input + 0xB0 + 0x17 + + + + + + [EqualizerRack1_[Channel2]_Effect1] + MixtrackPlatinumFX.deck[1].treble.input + 0xB1 + 0x17 + + + + + + [EqualizerRack1_[Channel3]_Effect1] + MixtrackPlatinumFX.deck[2].treble.input + 0xB2 + 0x17 + + + + + + [EqualizerRack1_[Channel4]_Effect1] + MixtrackPlatinumFX.deck[3].treble.input + 0xB3 + 0x17 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x18 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x18 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x18 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x18 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x18 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x18 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x18 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x18 + + + + + + [EqualizerRack1_[Channel1]_Effect1] + MixtrackPlatinumFX.deck[0].mid.input + 0xB0 + 0x18 + + + + + + [EqualizerRack1_[Channel2]_Effect1] + MixtrackPlatinumFX.deck[1].mid.input + 0xB1 + 0x18 + + + + + + [EqualizerRack1_[Channel3]_Effect1] + MixtrackPlatinumFX.deck[2].mid.input + 0xB2 + 0x18 + + + + + + [EqualizerRack1_[Channel4]_Effect1] + MixtrackPlatinumFX.deck[3].mid.input + 0xB3 + 0x18 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x19 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x19 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x19 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x19 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x19 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x19 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x19 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x19 + + + + + + [EqualizerRack1_[Channel1]_Effect1] + MixtrackPlatinumFX.deck[0].bass.input + 0xB0 + 0x19 + + + + + + [EqualizerRack1_[Channel2]_Effect1] + MixtrackPlatinumFX.deck[1].bass.input + 0xB1 + 0x19 + + + + + + [EqualizerRack1_[Channel3]_Effect1] + MixtrackPlatinumFX.deck[2].bass.input + 0xB2 + 0x19 + + + + + + [EqualizerRack1_[Channel4]_Effect1] + MixtrackPlatinumFX.deck[3].bass.input + 0xB3 + 0x19 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x1A + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x1A + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x1A + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x1A + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x1A + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x1A + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x1A + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x1A + + + + + + [QuickEffectRack1_[Channel1]] + MixtrackPlatinumFX.deck[0].filter.input + 0xB0 + 0x1A + + + + + + [QuickEffectRack1_[Channel2]] + MixtrackPlatinumFX.deck[1].filter.input + 0xB1 + 0x1A + + + + + + [QuickEffectRack1_[Channel3]] + MixtrackPlatinumFX.deck[2].filter.input + 0xB2 + 0x1A + + + + + + [QuickEffectRack1_[Channel4]] + MixtrackPlatinumFX.deck[3].filter.input + 0xB3 + 0x1A + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pflButton.input + 0x80 + 0x1B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pflButton.input + 0x81 + 0x1B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pflButton.input + 0x82 + 0x1B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pflButton.input + 0x83 + 0x1B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x1B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x1B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x1B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x1B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pflButton.input + 0x90 + 0x1B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pflButton.input + 0x91 + 0x1B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pflButton.input + 0x92 + 0x1B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pflButton.input + 0x93 + 0x1B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x1B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x1B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x1B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x1B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x1C + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x1C + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x1C + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x1C + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x1C + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x1C + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x1C + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x1C + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].volume.input + 0xB0 + 0x1C + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].volume.input + 0xB1 + 0x1C + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].volume.input + 0xB2 + 0x1C + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].volume.input + 0xB3 + 0x1C + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x1D + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x1D + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x1D + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x1D + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x1D + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x1D + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x1D + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x1D + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x1E + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x1E + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x1E + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x1E + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x1E + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x1E + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x1E + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x1E + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x1F + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x1F + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x1F + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x1F + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x1F + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x1F + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x1F + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x1F + + + + + + [Master] + MixtrackPlatinumFX.shiftToggle + The shift key. + 0x80 + 0x20 + + + + + + [Master] + MixtrackPlatinumFX.shiftToggle + The shift key. + 0x81 + 0x20 + + + + + + [Master] + MixtrackPlatinumFX.shiftToggle + The shift key. + 0x82 + 0x20 + + + + + + [Master] + MixtrackPlatinumFX.shiftToggle + The shift key. + 0x83 + 0x20 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x20 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x20 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x20 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x20 + + + + + + [Master] + MixtrackPlatinumFX.shiftToggle + The shift key. + 0x90 + 0x20 + + + + + + [Master] + MixtrackPlatinumFX.shiftToggle + The shift key. + 0x91 + 0x20 + + + + + + [Master] + MixtrackPlatinumFX.shiftToggle + The shift key. + 0x92 + 0x20 + + + + + + [Master] + MixtrackPlatinumFX.shiftToggle + The shift key. + 0x93 + 0x20 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x20 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x20 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x20 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x20 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x21 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x21 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x21 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x21 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x21 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x21 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x21 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x21 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x22 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x22 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x22 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x22 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x22 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x22 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x22 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x22 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x84 + 0x23 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x85 + 0x23 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x86 + 0x23 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x87 + 0x23 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].padSection.padPress + 0x94 + 0x23 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].padSection.padPress + 0x95 + 0x23 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].padSection.padPress + 0x96 + 0x23 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].padSection.padPress + 0x97 + 0x23 + + + + + + [Master] + MixtrackPlatinumFX.gains.mainGain.input + 0xBE + 0x23 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitch.inputLSB + 0xB0 + 0x29 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitch.inputLSB + 0xB1 + 0x29 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitch.inputLSB + 0xB2 + 0x29 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitch.inputLSB + 0xB3 + 0x29 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitchBendUp.input + 0x80 + 0x2B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitchBendUp.input + 0x81 + 0x2B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitchBendUp.input + 0x82 + 0x2B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitchBendUp.input + 0x83 + 0x2B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitchBendUp.input + 0x90 + 0x2B + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitchBendUp.input + 0x91 + 0x2B + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitchBendUp.input + 0x92 + 0x2B + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitchBendUp.input + 0x93 + 0x2B + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitchBendDown.input + 0x80 + 0x2C + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitchBendDown.input + 0x81 + 0x2C + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitchBendDown.input + 0x82 + 0x2C + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitchBendDown.input + 0x83 + 0x2C + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].pitchBendDown.input + 0x90 + 0x2C + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].pitchBendDown.input + 0x91 + 0x2C + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].pitchBendDown.input + 0x92 + 0x2C + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].pitchBendDown.input + 0x93 + 0x2C + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loopHalf.input + 0x84 + 0x34 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loopHalf.input + 0x85 + 0x34 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loopHalf.input + 0x86 + 0x34 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loopHalf.input + 0x87 + 0x34 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loopHalf.input + 0x94 + 0x34 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loopHalf.input + 0x95 + 0x34 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loopHalf.input + 0x96 + 0x34 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loopHalf.input + 0x97 + 0x34 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loopDouble.input + 0x84 + 0x35 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loopDouble.input + 0x85 + 0x35 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loopDouble.input + 0x86 + 0x35 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loopDouble.input + 0x87 + 0x35 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loopDouble.input + 0x94 + 0x35 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loopDouble.input + 0x95 + 0x35 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loopDouble.input + 0x96 + 0x35 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loopDouble.input + 0x97 + 0x35 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loopHalf.input + 0x84 + 0x36 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loopHalf.input + 0x85 + 0x36 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loopHalf.input + 0x86 + 0x36 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loopHalf.input + 0x87 + 0x36 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loopHalf.input + 0x94 + 0x36 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loopHalf.input + 0x95 + 0x36 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loopHalf.input + 0x96 + 0x36 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loopHalf.input + 0x97 + 0x36 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loopDouble.input + 0x84 + 0x37 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loopDouble.input + 0x85 + 0x37 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loopDouble.input + 0x86 + 0x37 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loopDouble.input + 0x87 + 0x37 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loopDouble.input + 0x94 + 0x37 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loopDouble.input + 0x95 + 0x37 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loopDouble.input + 0x96 + 0x37 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loopDouble.input + 0x97 + 0x37 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loop.input + 0x84 + 0x40 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loop.input + 0x85 + 0x40 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loop.input + 0x86 + 0x40 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loop.input + 0x87 + 0x40 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loop.input + 0x94 + 0x40 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loop.input + 0x95 + 0x40 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loop.input + 0x96 + 0x40 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loop.input + 0x97 + 0x40 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loop.input + 0x84 + 0x41 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loop.input + 0x85 + 0x41 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loop.input + 0x86 + 0x41 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loop.input + 0x87 + 0x41 + + + + + + [Channel1] + MixtrackPlatinumFX.deck[0].loop.input + 0x94 + 0x41 + + + + + + [Channel2] + MixtrackPlatinumFX.deck[1].loop.input + 0x95 + 0x41 + + + + + + [Channel3] + MixtrackPlatinumFX.deck[2].loop.input + 0x96 + 0x41 + + + + + + [Channel4] + MixtrackPlatinumFX.deck[3].loop.input + 0x97 + 0x41 + + + + + + + + [Channel1] + keylock + 0x90 + 0x0D + 0.5 + + + [Channel1] + keylock + 0x80 + 0x0D + 0.5 + + + [Channel2] + keylock + 0x91 + 0x0D + 0.5 + + + [Channel2] + keylock + 0x81 + 0x0D + 0.5 + + + [Channel3] + keylock + 0x92 + 0x0D + 0.5 + + + [Channel3] + keylock + 0x82 + 0x0D + 0.5 + + + [Channel4] + keylock + 0x93 + 0x0D + 0.5 + + + [Channel4] + keylock + 0x83 + 0x0D + 0.5 + + + + diff --git a/res/controllers/Numark-Mixtrack-Platinum-FX-scripts.js b/res/controllers/Numark-Mixtrack-Platinum-FX-scripts.js new file mode 100644 index 00000000000..92b213abb1b --- /dev/null +++ b/res/controllers/Numark-Mixtrack-Platinum-FX-scripts.js @@ -0,0 +1,1884 @@ +// eslint-disable-next-line no-var +var MixtrackPlatinumFX = {}; + +// FX +// FX toggles +MixtrackPlatinumFX.toggleFXControlEnable = true; +MixtrackPlatinumFX.toggleFXControlSuper = false; + +MixtrackPlatinumFX.shiftBrowseIsZoom = false; + +// setting this to false sets tap the file bpm, but without a way to reset its dangerous +MixtrackPlatinumFX.tapChangesTempo = true; + +// pitch ranges +// add/remove/modify steps to your liking +// default step must be set in Mixxx settings +// setting is stored per deck in pitchRange.currentRangeIdx +MixtrackPlatinumFX.pitchRanges = [0.08, 0.16, 0.5]; + +MixtrackPlatinumFX.HIGH_LIGHT = 0x7F; +MixtrackPlatinumFX.LOW_LIGHT = 0x01; + +// whether the corresponding Mixxx option is enabled +// (Settings -> Preferences -> Waveforms -> Synchronize zoom level across all waveforms) +MixtrackPlatinumFX.waveformsSynced = true; + +// jogwheel +MixtrackPlatinumFX.jogScratchSensitivity = 1024; +MixtrackPlatinumFX.jogScratchAlpha = 1; // do NOT set to 2 or higher +MixtrackPlatinumFX.jogScratchBeta = 1/32; +MixtrackPlatinumFX.jogPitchSensitivity = 10; +MixtrackPlatinumFX.jogSeekSensitivity = 10000; + +// blink settings +MixtrackPlatinumFX.enableBlink = true; +MixtrackPlatinumFX.blinkDelay = 700; + +// autoloop sizes, for available values see: +// https://manual.mixxx.org/2.3/en/chapters/appendix/mixxx_controls.html#control-[ChannelN]-beatloop_X_toggle +MixtrackPlatinumFX.autoLoopSizes = [ + "0.0625", + "0.125", + "0.25", + "0.5", + "1", + "2", + "4", + "8" +]; + +// beatjump values, for available values see: +// https://manual.mixxx.org/2.3/en/chapters/appendix/mixxx_controls.html#control-[ChannelN]-beatjump_X_forward +// underscores (_) at the end are needed because numeric values (e.g. 8) have two underscores (e.g. beatjump_8_forward), +// but "beatjump_forward"/"beatjump_backward" have only one underscore +MixtrackPlatinumFX.beatJumpValues = [ + "0.0625_", + "0.125_", + "0.25_", + "0.5_", + "1_", + "2_", + "", // "beatjump_forward"/"beatjump_backward" - jump by the value selected in Mixxx GUI (4 by default) + "8_" +]; + +// dim all lights when inactive instead of turning them off +components.Button.prototype.off = MixtrackPlatinumFX.LOW_LIGHT; + +// pad modes control codes +MixtrackPlatinumFX.PadModeControls = { + HOTCUE: 0x00, + AUTOLOOP: 0x0D, + FADERCUTS: 0x07, + SAMPLE1: 0x0B, + BEATJUMP: 0x01, // DUMMY not used by controller + SAMPLE2: 0x0F, + AUTOLOOP2: 0x0E, // DUMMY not used by controller + KEYPLAY: 0x0C, // DUMMY not used by controller + HOTCUE2: 0x02, + FADERCUTS2: 0x03, // DUMMY not used by controller + FADERCUTS3: 0x04, // DUMMY not used by controller + AUTOLOOP3: 0x05, // DUMMY not used by controller +}; + +// enables 4 bottom pads "fader cuts" for 8 +MixtrackPlatinumFX.faderCutSysex8 = [0xF0, 0x00, 0x20, 0x7F, 0x03, 0xF7]; +// enables only 4 top pads "fader cuts" +MixtrackPlatinumFX.faderCutSysex4 = [0xF0, 0x00, 0x20, 0x7F, 0x13, 0xF7]; + +// state variable, don't touch +MixtrackPlatinumFX.shifted = false; + +MixtrackPlatinumFX.initComplete=false; + +MixtrackPlatinumFX.bpms = []; +MixtrackPlatinumFX.trackBPM = function(value, group, _control) { + // file_bpm always seems to be 0? + // this doesn't work if we have to scan for bpm as it will be zero initially + // so we hook into the bpm change as well, and if we have 0 then set it to the first value seen (in bpm output) + MixtrackPlatinumFX.bpms[script.deckFromGroup(group) - 1] = engine.getValue(group, "bpm"); +}; + +MixtrackPlatinumFX.BlinkTimer=0; +MixtrackPlatinumFX.BlinkState=true; +MixtrackPlatinumFX.BlinkStateSlow=true; +MixtrackPlatinumFX.CallBacks=[]; +MixtrackPlatinumFX.CallSpeed=[]; +MixtrackPlatinumFX.BlinkStart = function(callback, slow) { + for (const i in MixtrackPlatinumFX.CallBacks) { + if (!MixtrackPlatinumFX.CallBacks[i]) { + // empty slot + MixtrackPlatinumFX.CallBacks[i]=callback; + MixtrackPlatinumFX.CallSpeed[i]=slow; + return Number(i)+1; + } + } + const idx = MixtrackPlatinumFX.CallBacks.push(callback); + MixtrackPlatinumFX.CallSpeed[idx-1]=slow; + return idx; +}; +MixtrackPlatinumFX.BlinkStop = function(index) { + MixtrackPlatinumFX.CallBacks[index-1]=null; +}; +MixtrackPlatinumFX.BlinkFunc = function() { + // toggle the global blink variables + MixtrackPlatinumFX.BlinkState = !MixtrackPlatinumFX.BlinkState; + if (MixtrackPlatinumFX.BlinkState) { + MixtrackPlatinumFX.BlinkStateSlow = !MixtrackPlatinumFX.BlinkStateSlow; + } + + // if we should be blinking the fx, then call its function + if (MixtrackPlatinumFX.FxBlinkState) { + MixtrackPlatinumFX.FxBlinkUpdateLEDs(); + } + // fire any callbacks + for (const i in MixtrackPlatinumFX.CallBacks) { + if (MixtrackPlatinumFX.CallBacks[i]) { + if (MixtrackPlatinumFX.CallSpeed[i]) { + if (MixtrackPlatinumFX.BlinkState) { + MixtrackPlatinumFX.CallBacks[i](MixtrackPlatinumFX.BlinkStateSlow); + } + } else { + MixtrackPlatinumFX.CallBacks[i](MixtrackPlatinumFX.BlinkState); + } + } + } +}; + +MixtrackPlatinumFX.init = function(id, debug) { + MixtrackPlatinumFX.id = id; + MixtrackPlatinumFX.debug = debug; + // print("init MixtrackPlatinumFX " + id + " debug: " + debug); + + // disable demo lightshow + const exitDemoSysex = [0xF0, 0x7E, 0x00, 0x06, 0x01, 0xF7]; + midi.sendSysexMsg(exitDemoSysex, exitDemoSysex.length); + + // status, extra 04 is just more device id, not sure what the 05 is + //F0 00 20 04 7F 03 01 05 F7 + + // wake (not sure what the extra 07 is for? + //F0 7E 00 07 06 01 F7 + + // I think these are the dial updates + //F0 00 20 04 7F 02 02 04 08 00 00 04 00 00 00 05 F7 + //F0 00 20 04 7F 04 01 04 00 00 00 04 00 00 00 05 F7 + //F0 00 20 04 7F 02 04 04 08 00 00 04 00 00 00 07 00 00 F7 + //F0 00 20 04 7F 03 02 04 08 00 00 04 00 00 00 05 F7 + //F0 00 20 04 7F 03 04 04 08 00 00 04 00 00 00 07 00 00 F7 + //F0 00 20 04 7F 01 04 04 08 00 00 04 00 00 00 07 00 00 F7 + //F0 00 20 04 7F 04 04 04 08 00 00 04 00 00 00 07 00 00 F7 + + // default to just the top 4 + midi.sendSysexMsg(MixtrackPlatinumFX.faderCutSysex4, MixtrackPlatinumFX.faderCutSysex4.length); + + // initialize component containers + MixtrackPlatinumFX.deck = new components.ComponentContainer(); + MixtrackPlatinumFX.effect = new components.ComponentContainer(); + let i; + for (i = 0; i < 4; i++) { + const group=`[Channel${ i+1 }]`; + MixtrackPlatinumFX.deck[i] = new MixtrackPlatinumFX.Deck(i + 1); + MixtrackPlatinumFX.updateRateRange(i, group, MixtrackPlatinumFX.pitchRanges[0]); + // refresh keylock state (the output mapping in the xml doesn't seem to do it + midi.sendShortMsg(0x80 | i, 0x0D, engine.getValue(group, "keylock")?0x7F:0x00); + midi.sendShortMsg(0x90 | i, 0x0D, engine.getValue(group, "keylock")?0x7F:0x00); + // Hook into this and save the bpm_file when loaded so we can reset it later + engine.makeConnection(group, "track_loaded", MixtrackPlatinumFX.trackBPM).trigger(); + } + for (i = 0; i < 2; i++) { + MixtrackPlatinumFX.effect[i] = new MixtrackPlatinumFX.EffectUnit((i % 2)+1); + } + // turn effect for master and headphones off to avoid confusion + engine.setValue("[EffectRack1_EffectUnit1]", "group_[Headphone]_enable", 0); + engine.setValue("[EffectRack1_EffectUnit2]", "group_[Headphone]_enable", 0); + engine.setValue("[EffectRack1_EffectUnit1]", "group_[Master]_enable", 0); + engine.setValue("[EffectRack1_EffectUnit2]", "group_[Master]_enable", 0); + + MixtrackPlatinumFX.browse = new MixtrackPlatinumFX.Browse(); + MixtrackPlatinumFX.gains = new MixtrackPlatinumFX.Gains(); + + const statusSysex = [0xF0, 0x00, 0x20, 0x7F, 0x03, 0x01, 0xF7]; + midi.sendSysexMsg(statusSysex, statusSysex.length); + + engine.makeConnection("[Channel1]", "VuMeter", MixtrackPlatinumFX.vuCallback); + engine.makeConnection("[Channel2]", "VuMeter", MixtrackPlatinumFX.vuCallback); + engine.makeConnection("[Channel3]", "VuMeter", MixtrackPlatinumFX.vuCallback); + engine.makeConnection("[Channel4]", "VuMeter", MixtrackPlatinumFX.vuCallback); + + engine.makeConnection("[Channel1]", "rate", MixtrackPlatinumFX.rateCallback).trigger(); + engine.makeConnection("[Channel2]", "rate", MixtrackPlatinumFX.rateCallback).trigger(); + engine.makeConnection("[Channel3]", "rate", MixtrackPlatinumFX.rateCallback).trigger(); + engine.makeConnection("[Channel4]", "rate", MixtrackPlatinumFX.rateCallback).trigger(); + + // trigger is needed to initialize lights to 0x01 + MixtrackPlatinumFX.deck.forEachComponent(function(component) { + component.trigger(); + }); + MixtrackPlatinumFX.effect.forEachComponent(function(component) { + component.trigger(); + }); + + // set FX buttons init light) + midi.sendShortMsg(0x98, 0x00, MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x98, 0x01, MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x98, 0x02, MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x99, 0x03, MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x99, 0x04, MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x99, 0x05, MixtrackPlatinumFX.LOW_LIGHT); + + // since we default to active on deck 1 and 2 make sure the controller does too + midi.sendShortMsg(0x90, 0x08, 0x7F); + midi.sendShortMsg(0x91, 0x08, 0x7F); + + // setup elapsed/remaining tracking + engine.makeConnection("[Controls]", "ShowDurationRemaining", MixtrackPlatinumFX.timeElapsedCallback); + MixtrackPlatinumFX.initComplete=true; + MixtrackPlatinumFX.updateArrows(true); + + MixtrackPlatinumFX.BlinkTimer = engine.beginTimer(MixtrackPlatinumFX.blinkDelay/2, MixtrackPlatinumFX.BlinkFunc); +}; + +MixtrackPlatinumFX.shutdown = function() { + const shutdownSysex = [0xF0, 0x00, 0x20, 0x7F, 0x02, 0xF7]; + let i; + + if (MixtrackPlatinumFX.BlinkTimer!==0) { + engine.stopTimer(MixtrackPlatinumFX.BlinkTimer); + MixtrackPlatinumFX.BlinkTimer=0; + } + + for (i=0; i<4; i++) { + // update spinner and position indicator + midi.sendShortMsg(0xB0 | i, 0x3F, 0); + midi.sendShortMsg(0xB0 | i, 0x06, 0); + // keylock indicator + midi.sendShortMsg(0x80 | i, 0x0D, 0x00); + // turn off bpm arrows + midi.sendShortMsg(0x80 | i, 0x0A, 0x00); // down arrow off + midi.sendShortMsg(0x80 | i, 0x09, 0x00); // up arrow off + + MixtrackPlatinumFX.sendScreenRateMidi(i+1, 0); + midi.sendShortMsg(0x90+i, 0x0e, 0); + MixtrackPlatinumFX.sendScreenBpmMidi(i+1, 0); + MixtrackPlatinumFX.sendScreenTimeMidi(i+1, 0); + MixtrackPlatinumFX.sendScreenDurationMidi(i+1, 0); + } + + // switch to decks 1 and 2 + midi.sendShortMsg(0x90, 0x08, 0x7F); + midi.sendShortMsg(0x91, 0x08, 0x7F); + + midi.sendSysexMsg(shutdownSysex, shutdownSysex.length); +}; + +MixtrackPlatinumFX.shift = function() { + MixtrackPlatinumFX.shifted = true; + MixtrackPlatinumFX.deck.shift(); + MixtrackPlatinumFX.browse.shift(); + MixtrackPlatinumFX.effect.shift(); + MixtrackPlatinumFX.gains.cueGain.shift(); +}; + +MixtrackPlatinumFX.unshift = function() { + MixtrackPlatinumFX.shifted = false; + MixtrackPlatinumFX.deck.unshift(); + MixtrackPlatinumFX.browse.unshift(); + MixtrackPlatinumFX.effect.unshift(); + MixtrackPlatinumFX.gains.cueGain.unshift(); +}; + +MixtrackPlatinumFX.allEffectOff = function() { + MixtrackPlatinumFX.effect[0].effects=[false, false, false]; + MixtrackPlatinumFX.effect[1].effects=[false, false, false]; + MixtrackPlatinumFX.FxBlinkUpdateLEDs(); + MixtrackPlatinumFX.effect[0].updateEffects(); + MixtrackPlatinumFX.effect[1].updateEffects(); +}; + +MixtrackPlatinumFX.FxBlinkUpdateLEDs = function() { + let newStates1=[false, false, false]; + let newStates2=[false, false, false]; + if (!MixtrackPlatinumFX.FxBlinkState || MixtrackPlatinumFX.BlinkState) { + newStates1=MixtrackPlatinumFX.effect[0].effects; + newStates2=MixtrackPlatinumFX.effect[1].effects; + } + midi.sendShortMsg(0x98, 0x00, newStates1[0] ? MixtrackPlatinumFX.HIGH_LIGHT:MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x98, 0x01, newStates1[1] ? MixtrackPlatinumFX.HIGH_LIGHT:MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x98, 0x02, newStates1[2] ? MixtrackPlatinumFX.HIGH_LIGHT:MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x99, 0x03, newStates2[0] ? MixtrackPlatinumFX.HIGH_LIGHT:MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x99, 0x04, newStates2[1] ? MixtrackPlatinumFX.HIGH_LIGHT:MixtrackPlatinumFX.LOW_LIGHT); + midi.sendShortMsg(0x99, 0x05, newStates2[2] ? MixtrackPlatinumFX.HIGH_LIGHT:MixtrackPlatinumFX.LOW_LIGHT); +}; + +MixtrackPlatinumFX.FxBlinkTimer=0; +MixtrackPlatinumFX.FxBlinkState=true; +MixtrackPlatinumFX.FxBlink = function() { + const start = MixtrackPlatinumFX.effect[0].isSwitchHolded || MixtrackPlatinumFX.effect[1].isSwitchHolded; + + if (start) { + MixtrackPlatinumFX.FxBlinkState = true; + MixtrackPlatinumFX.FxBlinkUpdateLEDs(); + } else { + // stop + MixtrackPlatinumFX.FxBlinkState = false; + MixtrackPlatinumFX.FxBlinkUpdateLEDs(); + } +}; + +// TODO in 2.3 it is not possible to "properly" map the FX selection buttons. +// this should be done with load_preset and QuickEffects instead (when effect +// chain preset saving/loading is available in Mixxx) +MixtrackPlatinumFX.EffectUnit = function(deckNumber) { + this.effects = [false, false, false]; + this.isSwitchHolded = false; + + this.updateEffects = function() { + if (MixtrackPlatinumFX.toggleFXControlEnable) { + for (let i = 1; i <= this.effects.length; i++) { + engine.setValue(`[EffectRack1_EffectUnit${ deckNumber }_Effect${i}]`, "enabled", this.effects[i-1]); + } + } + }; + + // switch values are: + // 0 - switch in the middle + // 1 - switch up + // 2 - switch down + this.enableSwitch = function(channel, control, value, status, group) { + this.isSwitchHolded = value !== 0; + + if (MixtrackPlatinumFX.toggleFXControlSuper) { + engine.setValue(group, "super1", Math.min(value, 1.0)); + } + + let fxDeck=deckNumber; + if (!MixtrackPlatinumFX.deck[deckNumber-1].active) { + fxDeck+=2; + } + engine.setValue("[EffectRack1_EffectUnit1]", `group_[Channel${ fxDeck }]_enable`, (value !== 0)); + engine.setValue("[EffectRack1_EffectUnit2]", `group_[Channel${ fxDeck }]_enable`, (value !== 0)); + + this.updateEffects(); + + MixtrackPlatinumFX.FxBlink(); + }; + + this.dryWetKnob = new components.Pot({ + group: `[EffectRack1_EffectUnit${ deckNumber }]`, + inKey: "mix" + }); + + this.effect1 = function(channel, control, value, status, _group) { + if (value === 0x7F) { + if (!MixtrackPlatinumFX.shifted) { + MixtrackPlatinumFX.allEffectOff(); + } + this.effects[0] = !this.effects[0]; + midi.sendShortMsg(status, control, this.effects[0] ? MixtrackPlatinumFX.HIGH_LIGHT : MixtrackPlatinumFX.LOW_LIGHT); + } + + + this.updateEffects(); + }; + + this.effect2 = function(channel, control, value, status, _group) { + if (value === 0x7F) { + if (!MixtrackPlatinumFX.shifted) { + MixtrackPlatinumFX.allEffectOff(); + } + this.effects[1] = !this.effects[1]; + midi.sendShortMsg(status, control, this.effects[1] ? MixtrackPlatinumFX.HIGH_LIGHT : MixtrackPlatinumFX.LOW_LIGHT); + } + + this.updateEffects(); + }; + + this.effect3 = function(channel, control, value, status, _group) { + if (value === 0x7F) { + if (!MixtrackPlatinumFX.shifted) { + MixtrackPlatinumFX.allEffectOff(); + } + this.effects[2] = !this.effects[2]; + midi.sendShortMsg(status, control, this.effects[2] ? MixtrackPlatinumFX.HIGH_LIGHT : MixtrackPlatinumFX.LOW_LIGHT); + } + + this.updateEffects(); + }; + + // copy paste since I'm not sure if we want to handle it like this or not + this.effectParam = new components.Encoder({ + group: `[EffectRack1_EffectUnit${ deckNumber }_Effect1]`, + shift: function() { + this.inKey = "meta"; + }, + unshift: function() { + this.inKey = "parameter1"; + }, + input: function(channel, control, value) { + this.inSetParameter(this.inGetParameter() + this.inValueScale(value)); + }, + inValueScale: function(value) { + return (value < 0x40) ? 0.05 : -0.05; + } + }); + this.effectParam2 = new components.Encoder({ + group: `[EffectRack1_EffectUnit${ deckNumber }_Effect2]`, + shift: function() { + this.inKey = "meta"; + }, + unshift: function() { + this.inKey = "parameter1"; + }, + input: function(channel, control, value) { + this.inSetParameter(this.inGetParameter() + this.inValueScale(value)); + }, + inValueScale: function(value) { + return (value < 0x40) ? 0.05 : -0.05; + } + }); + this.effectParam3 = new components.Encoder({ + group: `[EffectRack1_EffectUnit${ deckNumber }_Effect3]`, + shift: function() { + this.inKey = "meta"; + }, + unshift: function() { + this.inKey = "parameter1"; + }, + input: function(channel, control, value) { + this.inSetParameter(this.inGetParameter() + this.inValueScale(value)); + }, + inValueScale: function(value) { + return (value < 0x40) ? 0.05 : -0.05; + } + }); +}; + + +MixtrackPlatinumFX.EffectUnit.prototype = new components.ComponentContainer(); + +MixtrackPlatinumFX.activeForTap = function(value) { + // fuzzy logic + // to tap we probably want a playing deck + // and we probably don't want it "live" + // we will need it "active" + // so best is a playing deck with pfl = 5 + // next best a stopped deck with pfl = 4 + // then a playing deck = 3 + // then a stopped deck without pfl (which by this point is any loaded) = 2 + // and as a fallback a deck that isn't active + // if there are multiple then the lowest number wins (1,2,3,4) + // if no decks with loaded tracks then -1 so caller should check for that + let i=0; + let winner=-1; + let winnerScore=0; + for (i=0; i<4; i++) { + if (engine.getValue(`[Channel${ i+1 }]`, "track_loaded")) { + if (MixtrackPlatinumFX.deck[i].active) { + var localscore=0; + if (engine.getValue(`[Channel${ i+1 }]`, "pfl")) { + if (engine.getValue(`[Channel${ i+1 }]`, "play")) { + localscore=5; + } else { + localscore=4; + } + } else { + if (engine.getValue(`[Channel${ i+1 }]`, "play")) { + localscore=3; + } else { + localscore=2; + } + } + } else { + localscore=1; + } + if (localscore>winnerScore) { + winnerScore=localscore; + winner=i; + } + } + } + + if (winner>=0) { + if (value>0) { + MixtrackPlatinumFX.updateArrows(false, true, winner); + } else { + MixtrackPlatinumFX.updateArrows(true); + } + } + + return winner; +}; + +MixtrackPlatinumFX.Deck = function(number) { + components.Deck.call(this, number); + + const channel = number - 1; + const deck = this; + this.scratchModeEnabled = true; + this.active = (number === 1 || number === 2); + + this.setActive = function(active) { + this.active = active; + + if (!active) { + // trigger soft takeover on the pitch control + this.pitch.disconnect(); + } + }; + + this.bpm = new components.Component({ + outKey: "bpm", + output: function(value, group, _control) { + if (MixtrackPlatinumFX.bpms[script.deckFromGroup(group) - 1]===0) { + MixtrackPlatinumFX.bpms[script.deckFromGroup(group) - 1] = engine.getValue(group, "bpm"); + } + MixtrackPlatinumFX.sendScreenBpmMidi(number, Math.round(value * 100)); + }, + }); + + this.duration = new components.Component({ + outKey: "duration", + output: function(duration, _group, _control) { + // update duration + MixtrackPlatinumFX.sendScreenDurationMidi(number, duration * 1000); + + // when the duration changes, we need to update the play position + deck.position.trigger(); + }, + }); + + this.position = new components.Component({ + outKey: "playposition", + output: function(playposition, _group, _control) { + // the controller appears to expect a value in the range of 0-52 + // representing the position of the track. Here we send a message to the + // controller to update the position display with our current position. + let pos = Math.round(playposition * 52); + if (pos < 0) { + pos = 0; + } + midi.sendShortMsg(0xB0 | channel, 0x3F, pos); + + // get the current duration + const duration = deck.duration.outGetValue(); + + // update the time display + const time = MixtrackPlatinumFX.timeMs(number, playposition, duration); + MixtrackPlatinumFX.sendScreenTimeMidi(number, time); + + // update the spinner (range 64-115, 52 values) + // + // the visual spinner in the mixxx interface takes 1.8 seconds to loop + // (60 seconds/min divided by 33 1/3 revolutions per min) + const period = 60 / (33+1/3); + const midiResolution = 52; // the controller expects a value range of 64-115 + const timeElapsed = duration * playposition; + let spinner = Math.round(timeElapsed % period * (midiResolution / period)); + if (spinner < 0) { + spinner += 115; + } else { + spinner += 64; + } + + midi.sendShortMsg(0xB0 | channel, 0x06, spinner); + }, + }); + + this.playButton = new components.PlayButton({ + midi: [0x90 + channel, 0x00], + shiftControl: true, + sendShifted: true, + shiftOffset: 0x04, + }); + + this.playButtonbeatgrid = function(channel, control, value, status, group) { + engine.setValue(group, "beats_translate_curpos", value?1:0); + }; + + + this.cueButton = new components.CueButton({ + midi: [0x90 + channel, 0x01], + shiftControl: true, + sendShifted: true, + shiftOffset: 0x04 + }); + + this.syncButton = new components.SyncButton({ + midi: [0x90 + channel, 0x02], + shiftControl: true, + sendShifted: true, + shiftOffset: 0x01 + }); + + // we get two midi callbacks for tap, but double taps will be confusing so we just ignore the second set + if (number===1) { + if (MixtrackPlatinumFX.tapChangesTempo) { + this.tap = new components.Button({ + unshift: function() { + this.disconnect(); + this.input = function(channel, control, value, _status, _group) { + const tapch=MixtrackPlatinumFX.activeForTap(value)+1; + if (tapch) { + if (value>0) { + const prelen = bpm.tap.length; + const predelta = bpm.previousTapDelta; + bpm.tapButton(tapch); + // if the array reset, or changed then the tap was "accepted" + if ((bpm.tap.length===0) || (bpm.tap.length!==prelen) || predelta!==bpm.previousTapDelta) { + this.send(this.outValueScale(value)); + } else { + this.send(0); + } + } else { + this.send(this.outValueScale(value)); + } + } + }; + }, + shift: function() { + // reset rate to 0 (i.e. no tempo change) + this.disconnect(); + this.input = function(channel, control, value, _status, _group) { + const tapch=MixtrackPlatinumFX.activeForTap(value)+1; + if (value>0 && tapch) { + engine.setValue(`[Channel${ tapch }]`, "rate", 0); + } + }; + }, + midi: [0x88, 0x09] + }); + this.tap.output(0); + } else { + this.tap = new components.Button({ + shift: function() { + this.disconnect(); + this.input = function(channel, control, value, _status, _group) { + const tapch=MixtrackPlatinumFX.activeForTap(value)+1; + if (tapch) { + // This doesn't work, it doesn't set the bpm, it sets the rate to achieve this bpm + if (value>0 && MixtrackPlatinumFX.bpms[tapch-1]) { + engine.setValue(`[Channel${ tapch }]`, "bpm", MixtrackPlatinumFX.bpms[tapch-1]); + } + this.send(this.outValueScale(value)); + } + }; + }, + unshift: function() { + /* + this.disconnect(); + this.input = components.Button.prototype.input; + this.inKey = "bpm_tap"; + this.outKey = "bpm_tap"; + this.connect(); + this.trigger(); + */ + this.input = function(channel, control, value, _status, _group) { + const tapch=MixtrackPlatinumFX.activeForTap(value)+1; + if (tapch) { + engine.setValue(`[Channel${ tapch }]`, "bpm_tap", value); + this.send(this.outValueScale(value)); + } + }; + }, + //key: "bpm_tap", + midi: [0x88, 0x09] + }); + this.tap.output(0); + } + } else { + // ignore callbacks other than the first + this.tap = new components.Button({ + // null, ignore the second mapping + input: function(_channel, _control, _value, _status, _group) { }, + }); + } + + this.pflButton = new components.Button({ + shift: function() { + this.disconnect(); + this.inKey = "slip_enabled"; + this.outKey = "slip_enabled"; + this.connect(); + this.trigger(); + }, + unshift: function() { + this.disconnect(); + this.inKey = "pfl"; + this.outKey = "pfl"; + this.connect(); + this.trigger(); + }, + type: components.Button.prototype.types.toggle, + midi: [0x90 + channel, 0x1B], + }); + + this.loadButton = new components.Button({ + shift: function() { + this.inKey = "eject"; + }, + unshift: function() { + this.inKey = "LoadSelectedTrack"; + }, + }); + + this.volume = new components.Pot({ + inKey: "volume" + }); + + this.treble = new components.Pot({ + group: `[EqualizerRack1_${ this.currentDeck }_Effect1]`, + inKey: "parameter3" + }); + + this.mid = new components.Pot({ + group: `[EqualizerRack1_${ this.currentDeck }_Effect1]`, + inKey: "parameter2" + }); + + this.bass = new components.Pot({ + group: `[EqualizerRack1_${ this.currentDeck }_Effect1]`, + inKey: "parameter1" + }); + + this.filter = new components.Pot({ + group: `[QuickEffectRack1_${ this.currentDeck }]`, + inKey: "super1" + }); + + this.gain = new components.Pot({ + inKey: "pregain" + }); + + this.pitch = new components.Pot({ + inKey: "rate", + invert: true + }); + + this.padSection = new MixtrackPlatinumFX.PadSection(number); + + this.loop = new components.Button({ + outKey: "loop_enabled", + midi: [0x94 + channel, 0x40], + input: function(channel, control, value, status, group) { + if (!this.isPress(channel, control, value)) { + return; + } + + if (!MixtrackPlatinumFX.shifted) { + if (engine.getValue(group, "loop_enabled") === 0) { + script.triggerControl(group, "beatloop_activate"); + } else { + script.triggerControl(group, "beatlooproll_activate"); + } + } else { + if (engine.getValue(group, "loop_enabled") === 0) { + script.triggerControl(group, "reloop_toggle"); + } else { + script.triggerControl(group, "reloop_andstop"); + } + } + }, + shiftControl: true, + sendShifted: true, + shiftOffset: 0x01 + }); + + this.loopHalf = new components.Button({ + midi: [0x94 + channel, 0x34], + shiftControl: true, + sendShifted: true, + shiftOffset: 0x02, + shift: function() { + this.disconnect(); + this.inKey = "loop_in"; + this.outKey = "loop_in"; + this.connect(); + this.trigger(); + }, + unshift: function() { + this.disconnect(); + this.inKey = "loop_halve"; + this.outKey = "loop_halve"; + this.connect(); + this.trigger(); + } + }); + + this.loopDouble = new components.Button({ + midi: [0x94 + channel, 0x35], + shiftControl: true, + sendShifted: true, + shiftOffset: 0x02, + shift: function() { + this.disconnect(); + this.inKey = "loop_out"; + this.outKey = "loop_out"; + this.connect(); + this.trigger(); + }, + unshift: function() { + this.disconnect(); + this.inKey = "loop_double"; + this.outKey = "loop_double"; + this.connect(); + this.trigger(); + } + }); + + this.scratchToggle = new components.Button({ + // disconnects/connects are needed for the following scenario: + // 1. scratch mode is enabled (light on) + // 2. shift down + // 3. scratch button down + // 4. shift up + // 5. scratch button up + // scratch mode light is now off, should be on + key: "reverseroll", + midi: [0x90 + channel, 0x07], + unshift: function() { + this.disconnect(); // disconnect reverseroll light + this.input = function(channel, control, value) { + if (!this.isPress(channel, control, value)) { + return; + } + deck.scratchModeEnabled = !deck.scratchModeEnabled; + + // change the scratch mode status light + this.send(deck.scratchModeEnabled ? this.on : this.off); + }; + // set current scratch mode status light + this.send(deck.scratchModeEnabled ? this.on : this.off); + }, + sendShifted: false + }); + + this.pitchBendUp = new components.Button({ + shiftControl: true, + shiftOffset: 0x20, + shift: function() { + this.type = components.Button.prototype.types.toggle; + this.inKey = "keylock"; + }, + unshift: function() { + this.type = components.Button.prototype.types.push; + this.inKey = "rate_temp_up"; + } + }); + + this.pitchBendDown = new components.Button({ + currentRangeIdx: 0, + shift: function() { + this.input = function(channel, control, value) { + if (!this.isPress(channel, control, value)) { + return; + } + this.currentRangeIdx = (this.currentRangeIdx + 1) % MixtrackPlatinumFX.pitchRanges.length; + MixtrackPlatinumFX.updateRateRange(channel, this.group, MixtrackPlatinumFX.pitchRanges[this.currentRangeIdx]); + }; + }, + unshift: function() { + this.inKey = "rate_temp_down"; + this.input = components.Button.prototype.input; + } + }); + + this.setBeatgrid = new components.Button({ + key: "beats_translate_curpos", + midi: [0x98 + channel, 0x01 + (channel * 3)] + }); + + this.reconnectComponents(function(component) { + if (component.group === undefined) { + component.group = this.currentDeck; + } + }); +}; + +MixtrackPlatinumFX.Deck.prototype = new components.Deck(); + +MixtrackPlatinumFX.PadSection = function(deckNumber) { + components.ComponentContainer.call(this); + + this.blinkTimer = 0; + + this.longPressTimer = 0; + this.longPressMode = 0; + this.longPressHeld = false; + + // initialize leds + const ledOff = components.Button.prototype.off; + const ledOn = components.Button.prototype.on; + midi.sendShortMsg(0x93 + deckNumber, 0x00, ledOn); // hotcue + midi.sendShortMsg(0x93 + deckNumber, 0x0D, ledOff); // auto loop + midi.sendShortMsg(0x93 + deckNumber, 0x07, ledOff); // "fader cuts" + midi.sendShortMsg(0x93 + deckNumber, 0x0B, ledOff); // sample1 + + // shifted leds + midi.sendShortMsg(0x93 + deckNumber, 0x0F, ledOff); // sample2 + midi.sendShortMsg(0x93 + deckNumber, 0x02, ledOff); // beatjump + + this.modes = {}; + this.modes[MixtrackPlatinumFX.PadModeControls.HOTCUE] = new MixtrackPlatinumFX.ModeHotcue(deckNumber, false); + this.modes[MixtrackPlatinumFX.PadModeControls.AUTOLOOP] = new MixtrackPlatinumFX.ModeAutoLoop(deckNumber, false); + this.modes[MixtrackPlatinumFX.PadModeControls.FADERCUTS] = new MixtrackPlatinumFX.ModeFaderCuts(deckNumber, false); + this.modes[MixtrackPlatinumFX.PadModeControls.FADERCUTS2] = new MixtrackPlatinumFX.ModeFaderCuts(deckNumber, 1); + this.modes[MixtrackPlatinumFX.PadModeControls.FADERCUTS3] = new MixtrackPlatinumFX.ModeFaderCuts(deckNumber, 2); + this.modes[MixtrackPlatinumFX.PadModeControls.SAMPLE1] = new MixtrackPlatinumFX.ModeSample(deckNumber, false); + this.modes[MixtrackPlatinumFX.PadModeControls.BEATJUMP] = new MixtrackPlatinumFX.ModeBeatjump(deckNumber, 2); + this.modes[MixtrackPlatinumFX.PadModeControls.SAMPLE2] = new MixtrackPlatinumFX.ModeSample(deckNumber, 1); + this.modes[MixtrackPlatinumFX.PadModeControls.AUTOLOOP2] = new MixtrackPlatinumFX.ModeAutoLoop(deckNumber, 1); + this.modes[MixtrackPlatinumFX.PadModeControls.KEYPLAY] = new MixtrackPlatinumFX.ModeKeyPlay(deckNumber, 2); + this.modes[MixtrackPlatinumFX.PadModeControls.HOTCUE2] = new MixtrackPlatinumFX.ModeHotcue(deckNumber, 1); + this.modes[MixtrackPlatinumFX.PadModeControls.AUTOLOOP3] = new MixtrackPlatinumFX.ModeCueLoop(deckNumber, 2); + + this.modeButtonPress = function(channel, control, value) { + // always stop the time, its either the off, which should stop it + // or another button has been pressed, so that's now the "focus" + if (this.longPressTimer!==0) { + // release button, leave the timer going, but mark as not held so it won't go off (still using it for double press) + this.longPressHeld = false; + if (value === 0x7F) { + engine.stopTimer(this.longPressTimer); + // there was a time, see if its for this button, if it is then this is a double press so active the same as if it has been a long press + // cancel the timer eitherway + if (control===MixtrackPlatinumFX.PadModeControls.SAMPLE1 && this.longPressMode===MixtrackPlatinumFX.PadModeControls.KEYPLAY) { + this.setMode(channel, this.longPressMode); + this.longPressTimer = 0; + return; + } + if (control===MixtrackPlatinumFX.PadModeControls.HOTCUE && this.longPressMode===MixtrackPlatinumFX.PadModeControls.BEATJUMP) { + this.setMode(channel, this.longPressMode); + this.longPressTimer = 0; + return; + } + if (control===MixtrackPlatinumFX.PadModeControls.FADERCUTS && this.longPressMode===MixtrackPlatinumFX.PadModeControls.FADERCUTS3) { + this.setMode(channel, this.longPressMode); + this.longPressTimer = 0; + return; + } + if (control===MixtrackPlatinumFX.PadModeControls.AUTOLOOP && this.longPressMode===MixtrackPlatinumFX.PadModeControls.AUTOLOOP3) { + this.setMode(channel, this.longPressMode); + this.longPressTimer = 0; + return; + } + this.longPressTimer = 0; + } + } + + if (value !== 0x7F) { + return; + } + this.setMode(channel, control); + }; + + this.padPress = function(channel, control, value, status, group) { + const i = (control - 0x14) % 8; + this.currentMode.pads[i].input(channel, control, value, status, group); + }; + + this.setMode = function(channel, control) { + let ctrl2=control; + if (ctrl2===MixtrackPlatinumFX.PadModeControls.SAMPLE2 && this.currentMode.name===MixtrackPlatinumFX.PadModeControls.KEYPLAY) { + // this specific case we aren't setting a mode, we change the parameter for pitch play start + this.currentMode.nextRange(); + return; + } + // The mixer doesn't consider these to have shift, so we have to make it up by looking at shift and the original key + if (ctrl2===MixtrackPlatinumFX.PadModeControls.AUTOLOOP && MixtrackPlatinumFX.shifted) { + ctrl2=MixtrackPlatinumFX.PadModeControls.AUTOLOOP2; + } + if (ctrl2===MixtrackPlatinumFX.PadModeControls.FADERCUTS && MixtrackPlatinumFX.shifted) { + ctrl2=MixtrackPlatinumFX.PadModeControls.FADERCUTS2; + } + + // this stops the timeout from setting another timer! + if (this.longPressTimer===0) { + if (ctrl2===MixtrackPlatinumFX.PadModeControls.SAMPLE1 || ctrl2===MixtrackPlatinumFX.PadModeControls.HOTCUE || ctrl2===MixtrackPlatinumFX.PadModeControls.FADERCUTS || ctrl2===MixtrackPlatinumFX.PadModeControls.AUTOLOOP) { + if (ctrl2===MixtrackPlatinumFX.PadModeControls.AUTOLOOP) { + this.longPressMode=MixtrackPlatinumFX.PadModeControls.AUTOLOOP3; + } + if (ctrl2===MixtrackPlatinumFX.PadModeControls.SAMPLE1) { + this.longPressMode=MixtrackPlatinumFX.PadModeControls.KEYPLAY; + } + if (ctrl2===MixtrackPlatinumFX.PadModeControls.HOTCUE) { + this.longPressMode=MixtrackPlatinumFX.PadModeControls.BEATJUMP; + } + if (ctrl2===MixtrackPlatinumFX.PadModeControls.FADERCUTS) { + this.longPressMode=MixtrackPlatinumFX.PadModeControls.FADERCUTS3; + } + this.longPressHeld = true; + + const thirdaryMode = this; // Can't use 'this' in function below + this.longPressTimer = engine.beginTimer(components.Button.prototype.longPressTimeout*2, function() { + if (thirdaryMode.longPressHeld) { + thirdaryMode.setMode(channel, thirdaryMode.longPressMode); + } + thirdaryMode.longPressTimer = 0; + thirdaryMode.longPressHeld = false; + }, true); + } + } + + const newMode = this.modes[ctrl2]; + if ((this.currentMode.control === newMode.control) && (this.currentMode.secondaryMode === newMode.secondaryMode)) { + return; // selected mode already set, no need to change anything + } + + this.currentMode.forEachComponent(function(component) { + component.disconnect(); + }); + + // set the correct shift state for new mode + if (this.isShifted) { + newMode.shift(); + } else { + newMode.unshift(); + } + + newMode.forEachComponent(function(component) { + component.connect(); + component.trigger(); + }); + if (newMode.activate) { + newMode.activate(); + } + + if (MixtrackPlatinumFX.enableBlink) { + // stop blinking if old mode was secondary mode + if (this.currentMode.secondaryMode) { + this.blinkLedOff(); + + // disable light on the old control in case it ended up in 0x7F state + midi.sendShortMsg(0x90 + channel, this.currentMode.unshiftedControl, 0x01); + } + + // start blinking if new mode is a secondary mode + if (newMode.secondaryMode) { + this.blinkLedOn(0x90 + channel, newMode.unshiftedControl, newMode.lightOnValue, newMode.secondaryMode); + } + } + + // light off on old mode select button + midi.sendShortMsg(0x90 + channel, this.currentMode.control, 0x01); + + // light on on new mode select button + midi.sendShortMsg(0x90 + channel, newMode.control, newMode.lightOnValue); + + this.currentMode = newMode; + }; + + // start an infinite timer that toggles led state + this.blinkLedOn = function(midi1, midi2, onVal, secondMode) { + this.blinkLedOff(); + this.blinkTimer = MixtrackPlatinumFX.BlinkStart(function(isOn) { + midi.sendShortMsg(midi1, midi2, isOn ? onVal : 0x01); + }, (secondMode!==2)); + }; + + // stop the blink timer + this.blinkLedOff = function() { + if (this.blinkTimer === 0) { + return; + } + + MixtrackPlatinumFX.BlinkStop(this.blinkTimer); + this.blinkTimer = 0; + }; + + this.disablePadLights = function() { + for (let i = 0; i < 16; i++) { // 0-7 = unshifted; 8-15 = shifted + midi.sendShortMsg(0x93 + deckNumber, 0x14 + i, 0x01); + } + }; + + this.currentMode = this.modes[MixtrackPlatinumFX.PadModeControls.HOTCUE]; +}; +MixtrackPlatinumFX.PadSection.prototype = Object.create(components.ComponentContainer.prototype); + +MixtrackPlatinumFX.ModeHotcue = function(deckNumber, secondaryMode) { + components.ComponentContainer.call(this); + + this.control = MixtrackPlatinumFX.PadModeControls.HOTCUE; + this.unshiftedControl = MixtrackPlatinumFX.PadModeControls.HOTCUE; + this.secondaryMode = secondaryMode; + this.lightOnValue = 0x7F; + + this.name = MixtrackPlatinumFX.PadModeControls.HOTCUE; + let offset=0; + if (secondaryMode===1) { + this.name = MixtrackPlatinumFX.PadModeControls.HOTCUE2; + this.control = MixtrackPlatinumFX.PadModeControls.HOTCUE2; + offset=8; + } + this.pads = new components.ComponentContainer(); + for (let i = 0; i < 8; i++) { + this.pads[i] = new components.HotcueButton({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + number: i + 1 + offset, + shiftControl: true, + sendShifted: true, + shiftOffset: 0x08, + outConnect: false + }); + } +}; +MixtrackPlatinumFX.ModeHotcue.prototype = Object.create(components.ComponentContainer.prototype); + +MixtrackPlatinumFX.ModeAutoLoop = function(deckNumber, secondaryMode) { + components.ComponentContainer.call(this); + + this.name = MixtrackPlatinumFX.PadModeControls.AUTOLOOP; + if (secondaryMode) { + this.name = MixtrackPlatinumFX.PadModeControls.AUTOLOOP2; + } + this.control = MixtrackPlatinumFX.PadModeControls.AUTOLOOP; + this.unshiftedControl = MixtrackPlatinumFX.PadModeControls.AUTOLOOP; + this.secondaryMode = secondaryMode; + this.lightOnValue = 0x7F; + + this.pads = new components.ComponentContainer(); + for (let i = 0; i < 8; i++) { + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + size: MixtrackPlatinumFX.autoLoopSizes[i], + shiftControl: true, + sendShifted: true, + shiftOffset: 0x08, + shift: function() { + if (!secondaryMode) { + this.inKey = `beatlooproll_${ this.size }_activate`; + this.outKey = `beatlooproll_${ this.size }_activate`; + } else { + this.inKey = `beatloop_${ this.size }_toggle`; + this.outKey = `beatloop_${ this.size }_enabled`; + } + }, + unshift: function() { + if (!secondaryMode) { + this.inKey = `beatloop_${ this.size }_toggle`; + this.outKey = `beatloop_${ this.size }_enabled`; + } else { + this.inKey = `beatlooproll_${ this.size }_activate`; + this.outKey = `beatlooproll_${ this.size }_activate`; + } + }, + outConnect: false + }); + } +}; +MixtrackPlatinumFX.ModeAutoLoop.prototype = Object.create(components.ComponentContainer.prototype); + +MixtrackPlatinumFX.ModeCueLoop = function(deckNumber, secondaryMode) { + components.ComponentContainer.call(this); + + this.name = MixtrackPlatinumFX.PadModeControls.AUTOLOOP; + if (secondaryMode) { + this.name = MixtrackPlatinumFX.PadModeControls.AUTOLOOP3; + } + this.control = MixtrackPlatinumFX.PadModeControls.AUTOLOOP; + this.unshiftedControl = MixtrackPlatinumFX.PadModeControls.AUTOLOOP; + this.secondaryMode = secondaryMode; + this.lightOnValue = 0x7F; + + this.pads = new components.ComponentContainer(); + for (let i = 0; i < 8; i++) { + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + size: MixtrackPlatinumFX.autoLoopSizes[i], + shiftControl: true, + sendShifted: true, + shiftOffset: 0x08, + keynum: i, + shift: function() { + this.input=function(channel, control, value, _status, _group) { + engine.setValue(this.group, "slip_enabled", value); + engine.setValue(this.group, `hotcue_${ this.keynum+1 }_activate`, value); + engine.setValue(this.group, "beatlooproll_activate", value); + midi.sendShortMsg(this.midi[0], this.midi[1] + this.shiftOffset, this.outValueScale(engine.getValue(this.group, `hotcue_${ this.keynum+1 }_enabled`))); + }; + midi.sendShortMsg(this.midi[0], this.midi[1] + this.shiftOffset, this.outValueScale(engine.getValue(this.group, `hotcue_${ this.keynum+1 }_enabled`))); + }, + unshift: function() { + this.input=function(channel, control, value, _status, _group) { + if (value > 0) { + engine.setValue(this.group, `hotcue_${ this.keynum+1 }_activate`, value); + script.triggerControl(this.group, "beatloop_activate"); + } else { + engine.setValue(this.group, `hotcue_${ this.keynum+1 }_activate`, value); + } + midi.sendShortMsg(this.midi[0], this.midi[1], this.outValueScale(engine.getValue(this.group, `hotcue_${ this.keynum+1 }_enabled`))); + }; + midi.sendShortMsg(this.midi[0], this.midi[1], this.outValueScale(engine.getValue(this.group, `hotcue_${ this.keynum+1 }_enabled`))); + }, + outConnect: false + }); + } +}; +MixtrackPlatinumFX.ModeCueLoop.prototype = Object.create(components.ComponentContainer.prototype); + +MixtrackPlatinumFX.mykey=0; +MixtrackPlatinumFX.ModeKeyPlay = function(deckNumber, secondaryMode) { + components.ComponentContainer.call(this); + + this.name = MixtrackPlatinumFX.PadModeControls.KEYPLAY; + this.control = MixtrackPlatinumFX.PadModeControls.SAMPLE1; + this.unshiftedControl = MixtrackPlatinumFX.PadModeControls.SAMPLE1; + this.secondaryMode = secondaryMode; + this.lightOnValue = 0x7F; + + this.nextRange = function() { + switch (this.pads.keyshiftStart) { + case 0: + this.pads.keyshiftStart=4; + break; + case 4: + this.pads.keyshiftStart=7; + break; + case 7: + this.pads.keyshiftStart=0; + break; + default: + this.pads.keyshiftStart=4; + break; + } + }; + + this.pads = new components.ComponentContainer(); + const parentPads_ = this.pads; + this.pads.cueP=1; + this.pads.keyshiftStart=4; + for (let i = 0; i < 8; i++) { + this.pads[i] = new components.Button({ + parentPads: parentPads_, + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + shiftOffset: 0x08, + keynum: i, + shift: function() { + this.input=function(channel, control, value, _status, _group) { + if (value>0) { + this.parentPads.cueP=this.keynum+1; + } + }; + this.off=components.Button.prototype.off; + midi.sendShortMsg(this.midi[0], this.midi[1] + this.shiftOffset, this.outValueScale(engine.getValue(this.group, `hotcue_${ this.keynum+1 }_enabled`))); + }, + unshift: function() { + // serato has them the opposite way to how I'd expect so shift the two rows around + const thiskeynum=((this.keynum+4)%8); + this.thiskey=thiskeynum-this.parentPads.keyshiftStart; + this.input=function(channel, control, value, _status, _group) { + if (value>0) { + engine.setValue(this.group, "pitch_adjust", this.thiskey); + MixtrackPlatinumFX.mykey=this.keynum; + this.parentPads.forEachComponent(function(apad) { apad.output(0); }); + this.output(value); + } else { + if (this.keynum===MixtrackPlatinumFX.mykey) { + //engine.setValue(this.group,"key",engine.getValue(this.group,"file_key")); + } + } + engine.setValue(this.group, `hotcue_${ this.parentPads.cueP }_activate`, value); + }; + this.off=thiskeynum===this.parentPads.keyshiftStart?5:components.Button.prototype.off; + if (engine.getValue(this.group, "pitch_adjust")===this.thiskey) { + this.output(0x7F); + } else { + this.output(0); + } + }, + trigger: function() { + this.output(0); + + }, + }); + } +}; +MixtrackPlatinumFX.ModeKeyPlay.prototype = Object.create(components.ComponentContainer.prototype); + +// when pads are in "fader cuts" mode, they rapidly move the crossfader. +// holding a pad activates a "fader cut", releasing it causes the GUI crossfader +// to return to the position of physical crossfader +MixtrackPlatinumFX.ModeFaderCuts = function(deckNumber, secondaryMode) { + components.ComponentContainer.call(this); + + this.name = MixtrackPlatinumFX.PadModeControls.FADERCUTS; + if (secondaryMode===1) { + this.name = MixtrackPlatinumFX.PadModeControls.FADERCUTS2; + } + if (secondaryMode===2) { + this.name = MixtrackPlatinumFX.PadModeControls.FADERCUTS3; + } + this.control = MixtrackPlatinumFX.PadModeControls.FADERCUTS; + this.unshiftedControl = MixtrackPlatinumFX.PadModeControls.FADERCUTS; + this.secondaryMode = secondaryMode; + this.lightOnValue = 0x09; // for "fader cuts" 0x09 works better than 0x7F for some reason (0x7F turns the other lamps to a bit brighter) + + this.activate = function() { + if (this.secondaryMode===1) { + midi.sendSysexMsg(MixtrackPlatinumFX.faderCutSysex8, MixtrackPlatinumFX.faderCutSysex8.length); + } else { + midi.sendSysexMsg(MixtrackPlatinumFX.faderCutSysex4, MixtrackPlatinumFX.faderCutSysex4.length); + } + }; + + // fadercut pads are controlled by hardware of firmware in this mode + let numFader=4; + if (secondaryMode===1) { + numFader=8; + } + this.pads = new components.ComponentContainer(); + let i; + for (i = 0; i < numFader; i++) { + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + input: function(channel, control, value, _status, _group) { + this.output(value); + }, + trigger: function() { + // in "fader cuts" mode pad lights need to be disabled manually, + // as pads are controlled by hardware or firmware in this mode + // and don't have associated controls. without this, lights from + // previously selected mode would still be on after changing mode + // to "fader cuts" + this.output(0); + }, + outConnect: false, + }); + } + if (secondaryMode===false) { + i=4; + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + key: "play_stutter", + outConnect: false, + }); + i++; + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + key: "start", + outConnect: false, + }); + i++; + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + key: "back", + outConnect: false, + }); + i++; + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + key: "fwd", + outConnect: false, + }); + } + if (secondaryMode===2) { + i=4; + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + key: "reverseroll", + outConnect: false, + }); + i++; + this.pads[i] = new components.Button({ + type: 2, + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + key: "reverse", + outConnect: false, + }); + i++; + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + shift: function() { + this.disconnect(); + this.inKey = "reset_key"; + this.outKey = "reset_key"; + }, + unshift: function() { + this.disconnect(); + this.inKey = "sync_key"; + this.outKey = "sync_key"; + }, + outConnect: false, + }); + i++; + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + outConnect: false, + unshift: function() { + this.disconnect(); + this.input = function(channel, control, value, _status, _group) { + if (value>0) { + const prelen = bpm.tap.length; + const predelta = bpm.previousTapDelta; + bpm.tapButton(deckNumber); + // if the array reset, or changed then the tap was "accepted" + if ((bpm.tap.length===0) || (bpm.tap.length!==prelen) || predelta!==bpm.previousTapDelta) { + this.send(this.outValueScale(value)); + } else { + this.send(0); + } + } else { + this.send(this.outValueScale(value)); + } + }; + }, + shift: function() { + // reset rate to 0 (i.e. no tempo change) + this.disconnect(); + this.input = function(channel, control, value, _status, _group) { + if (value>0) { + engine.setValue(this.group, "rate", 0); + } + }; + }, + }); + } +}; + +MixtrackPlatinumFX.ModeFaderCuts.prototype = Object.create(components.ComponentContainer.prototype); + +MixtrackPlatinumFX.ModeSample = function(deckNumber, secondaryMode) { + components.ComponentContainer.call(this); + + if (!secondaryMode) { + // samples 1-8 + this.name = MixtrackPlatinumFX.PadModeControls.SAMPLE1; + this.control = MixtrackPlatinumFX.PadModeControls.SAMPLE1; + this.firstSampleNumber = 1; + } else { + // samples 9-16 + this.name = MixtrackPlatinumFX.PadModeControls.SAMPLE2; + this.control = MixtrackPlatinumFX.PadModeControls.SAMPLE2; + this.unshiftedControl = MixtrackPlatinumFX.PadModeControls.SAMPLE1; + this.firstSampleNumber = 9; + } + this.secondaryMode = secondaryMode; + this.lightOnValue = 0x7F; + + this.pads = new components.ComponentContainer(); + for (let i = 0; i < 8; i++) { + this.pads[i] = new components.SamplerButton({ + midi: [0x93 + deckNumber, 0x14 + i], + number: this.firstSampleNumber + i, + shiftControl: true, + sendShifted: true, + shiftOffset: 0x08, + outConnect: false, + loaded: 0x05, + looping: 0x0F, + playing: 0x0F, + }); + } +}; +MixtrackPlatinumFX.ModeSample.prototype = Object.create(components.ComponentContainer.prototype); + +MixtrackPlatinumFX.ModeBeatjump = function(deckNumber, secondaryMode) { + components.ComponentContainer.call(this); + + this.name = MixtrackPlatinumFX.PadModeControls.BEATJUMP; + this.control = MixtrackPlatinumFX.PadModeControls.HOTCUE; + this.secondaryMode = secondaryMode; + this.unshiftedControl = MixtrackPlatinumFX.PadModeControls.HOTCUE; + this.lightOnValue = 0x7F; + + this.pads = new components.ComponentContainer(); + for (let i = 0; i < 8; i++) { + this.pads[i] = new components.Button({ + group: `[Channel${ deckNumber }]`, + midi: [0x93 + deckNumber, 0x14 + i], + size: MixtrackPlatinumFX.beatJumpValues[i], + shiftControl: true, + sendShifted: true, + shiftOffset: 0x08, + shift: function() { + this.disconnect(); + this.inKey = `beatjump_${ this.size }backward`; + this.outKey = `beatjump_${ this.size }backward`; + this.connect(); + this.trigger(); + }, + unshift: function() { + this.disconnect(); + this.inKey = `beatjump_${ this.size }forward`; + this.outKey = `beatjump_${ this.size }forward`; + this.connect(); + this.trigger(); + }, + outConnect: false + }); + } +}; +MixtrackPlatinumFX.ModeBeatjump.prototype = Object.create(components.ComponentContainer.prototype); + +MixtrackPlatinumFX.Browse = function() { + this.knob = new components.Encoder({ + speed: 0, + speedTimer: 0, + shiftControl: true, + shiftOffset: 0x01, + input: function(channel, control, value) { + let direction; + if (MixtrackPlatinumFX.shifted && MixtrackPlatinumFX.shiftBrowseIsZoom) { + direction = (value > 0x40) ? "up" : "down"; + engine.setParameter("[Channel1]", `waveform_zoom_${ direction}`, 1); + + // need to zoom both channels if waveform sync is disabled in Mixxx settings. + // and when it's enabled then no need to zoom 2nd channel, as it will cause + // the zoom to jump 2 levels at once + if (!MixtrackPlatinumFX.waveformsSynced) { + engine.setParameter("[Channel2]", `waveform_zoom_${ direction}`, 1); + } + } else { + if (this.speedTimer !== 0) { + engine.stopTimer(this.speedTimer); + this.speedTimer = 0; + } + this.speedTimer = engine.beginTimer(100, function() { + this.speed=0; + this.speedTimer = 0; + }, true); + this.speed++; + direction = (value > 0x40) ? value - 0x80 : value; + if (MixtrackPlatinumFX.shifted) { + // when shifted go fast (consecutive squared!) + direction *= this.speed*this.speed; + } else { + // normal, up to 3 consecutive do one for fine control, then speed up + if (this.speed>3) { direction *= Math.min(4, (this.speed-3)); } + } + engine.setParameter("[Library]", "MoveVertical", direction); + } + } + }); + + this.knobButton = new components.Button({ + group: "[Library]", + shiftControl: true, + shiftOffset: 0x01, + previewing: false, + shift: function() { + this.inKey = "GoToItem"; + this.input = function(channel, control, value, _status, _group) { + if (value>0) { + if (MixtrackPlatinumFX.rightShift) { + if (this.previewing) { + script.triggerControl("[PreviewDeck1]", "stop"); + this.previewing = false; + } else { + script.triggerControl("[PreviewDeck1]", "LoadSelectedTrackAndPlay"); + this.previewing = true; + } + } else { + script.triggerControl("[Library]", "GoToItem"); + } + } + }; + }, + unshift: function() { + this.input = components.Button.prototype.input; + this.inKey = "MoveFocusForward"; + } + }); +}; +MixtrackPlatinumFX.Browse.prototype = new components.ComponentContainer(); + +MixtrackPlatinumFX.Gains = function() { + this.mainGain = new components.Pot({ + group: "[Master]", + inKey: "gain" + }); + + this.cueGain = new components.Pot({ + group: "[Master]", + inKey: "headGain", + shift: function() { + this.disconnect(); + this.group = "[Sampler1]"; + this.inKey = "pregain"; + this.input = function(channel, control, value, _status, _group) { + const newValue = this.inValueScale(value); + for (let i=1; i<=16; i++) { + engine.setParameter(`[Sampler${ i }]`, "pregain", newValue); + } + }; + }, + unshift: function() { + this.disconnect(); + this.firstValueReceived=false; + this.group = "[Master]"; + this.inKey = "headGain"; + this.input = components.Pot.prototype.input; + }, + }); + + this.cueMix = new components.Pot({ + group: "[Master]", + inKey: "headMix" + }); +}; +MixtrackPlatinumFX.Gains.prototype = new components.ComponentContainer(); + +MixtrackPlatinumFX.vuCallback = function(value, group) { + const level = value * 90; + const deckOffset = script.deckFromGroup(group) - 1; + midi.sendShortMsg(0xB0 + deckOffset, 0x1F, level); +}; + +MixtrackPlatinumFX.wheelTouch = function(channel, control, value) { + const deckNumber = channel + 1; + + if (!MixtrackPlatinumFX.shifted && MixtrackPlatinumFX.deck[channel].scratchModeEnabled && value === 0x7F) { + // touch start + + engine.scratchEnable(deckNumber, MixtrackPlatinumFX.jogScratchSensitivity, 33+1/3, MixtrackPlatinumFX.jogScratchAlpha, MixtrackPlatinumFX.jogScratchBeta, true); + } else if (value === 0) { + // touch end + engine.scratchDisable(deckNumber, true); + } +}; + +MixtrackPlatinumFX.wheelTurn = function(channel, control, value, status, group) { + const deckNumber = channel + 1; + + let newValue = value; + + if (value >= 64) { + // correct the value if going backwards + newValue -= 128; + } + + if (MixtrackPlatinumFX.shifted) { + // seek + const oldPos = engine.getValue(group, "playposition"); + + engine.setValue(group, "playposition", oldPos + newValue / MixtrackPlatinumFX.jogSeekSensitivity); + } else if (MixtrackPlatinumFX.deck[channel].scratchModeEnabled && engine.isScratching(deckNumber)) { + // scratch + engine.scratchTick(deckNumber, newValue); + } else { + // pitch bend + engine.setValue(group, "jog", newValue / MixtrackPlatinumFX.jogPitchSensitivity); + } +}; + +MixtrackPlatinumFX.timeElapsedCallback = function(value, _group, _control) { + // 0 = elapsed + // 1 = remaining + // 2 = both (we ignore this as the controller can't show both) + let onoff; + if (value === 0) { + // show elapsed + onoff = 0x00; + } else if (value === 1) { + // show remaining + onoff = 0x7F; + } else { + // both, ignore the event + return; + } + + // update all 4 decks on the controller + midi.sendShortMsg(0x90, 0x46, onoff); + midi.sendShortMsg(0x91, 0x46, onoff); + midi.sendShortMsg(0x92, 0x46, onoff); + midi.sendShortMsg(0x93, 0x46, onoff); +}; + +MixtrackPlatinumFX.timeMs = function(deck, position, duration) { + return Math.round(duration * position * 1000); +}; + +MixtrackPlatinumFX.encodeNumToArray = function(number, drop, unsigned) { + const numberarray = [ + (number >> 28) & 0x0F, + (number >> 24) & 0x0F, + (number >> 20) & 0x0F, + (number >> 16) & 0x0F, + (number >> 12) & 0x0F, + (number >> 8) & 0x0F, + (number >> 4) & 0x0F, + number & 0x0F, + ]; + + if (drop !== undefined) { + numberarray.splice(0, drop); + } + + if (number < 0) { numberarray[0] = 0x07; } else if (!unsigned) { numberarray[0] = 0x08; } + + return numberarray; +}; + +MixtrackPlatinumFX.sendScreenDurationMidi = function(deck, duration) { + if (duration < 1) { + duration = 1; + } + const durationArray = MixtrackPlatinumFX.encodeNumToArray(duration - 1); + + const bytePrefix = [0xF0, 0x00, 0x20, 0x7F, deck, 0x03]; + const bytePostfix = [0xF7]; + const byteArray = bytePrefix.concat(durationArray, bytePostfix); + midi.sendSysexMsg(byteArray, byteArray.length); +}; + +MixtrackPlatinumFX.sendScreenTimeMidi = function(deck, time) { + const timeArray = MixtrackPlatinumFX.encodeNumToArray(time); + + const bytePrefix = [0xF0, 0x00, 0x20, 0x7F, deck, 0x04]; + const bytePostfix = [0xF7]; + const byteArray = bytePrefix.concat(timeArray, bytePostfix); + midi.sendSysexMsg(byteArray, byteArray.length); +}; + +MixtrackPlatinumFX.sendScreenBpmMidi = function(deck, bpm) { + const bpmArray = MixtrackPlatinumFX.encodeNumToArray(bpm); + bpmArray.shift(); + bpmArray.shift(); + + const bytePrefix = [0xF0, 0x00, 0x20, 0x7F, deck, 0x01]; + const bytePostfix = [0xF7]; + const byteArray = bytePrefix.concat(bpmArray, bytePostfix); + midi.sendSysexMsg(byteArray, byteArray.length); + + MixtrackPlatinumFX.updateArrows(); +}; + +MixtrackPlatinumFX.rightShift=false; +MixtrackPlatinumFX.shiftToggle = function(channel, control, value, status, _group) { + if (value === 0x7F) { + if (status===0x91 || status===0x93) { + MixtrackPlatinumFX.rightShift=true; + } + MixtrackPlatinumFX.shift(); + } else { + MixtrackPlatinumFX.rightShift=false; + MixtrackPlatinumFX.unshift(); + } +}; + +MixtrackPlatinumFX.deckSwitch = function(channel, control, value, _status, _group) { + // Ignore the release the deck switch callback + // called both when actually releasing the button and for the alt deck when switching + if (value) { + const deck = channel; + MixtrackPlatinumFX.deck[deck].setActive(value === 0x7F); + // turn "off" the other deck + // this can't reliably be done with the release as it also trigger for this deck when the button is released + let other = 4-deck; + if (deck===0 || deck===2) { other = 2-deck; } + MixtrackPlatinumFX.deck[other].setActive(false); + // also zero vu meters + if (value === 0x7F) { + midi.sendShortMsg(0xBF, 0x44, 0); + midi.sendShortMsg(0xBF, 0x45, 0); + } + MixtrackPlatinumFX.updateArrows(true); + } +}; + +var sendSysex = function(buffer) { + midi.sendSysexMsg(buffer, buffer.length); +}; + +MixtrackPlatinumFX.sendScreenRateMidi = function(deck, rate) { + const rateArray = MixtrackPlatinumFX.encodeNumToArray(rate, 2); + + const bytePrefix = [0xF0, 0x00, 0x20, 0x7F, deck, 0x02]; + const bytePostfix = [0xF7]; + const byteArray = bytePrefix.concat(rateArray, bytePostfix); + sendSysex(byteArray); +}; + +// arrow data state (and cache to prevent midi spam) +MixtrackPlatinumFX.arrowsData = { + arrowsUpdateOn: true, + uparrow: [0, 0, 0, 0], + downarrow: [0, 0, 0, 0], +}; + +// force refresh turns arrow behaviour back to normal, and forces a refresh bypressing the cache +// force show turns both arrows on and suspends normal operation +MixtrackPlatinumFX.updateArrows = function(forceRefresh, forceShow, deck) { + if (!MixtrackPlatinumFX.initComplete) { + return; + } + + if (forceShow) { + // both arrows on to indicate the deck we are tapping + midi.sendShortMsg(0x80 | deck, 0x0A, 1); + midi.sendShortMsg(0x80 | deck, 0x09, 1); + // and stop other updates changing them + MixtrackPlatinumFX.arrowsData.arrowsUpdateOn=false; + } else { + if (forceRefresh) { + MixtrackPlatinumFX.arrowsData.arrowsUpdateOn=true; + } + if (MixtrackPlatinumFX.arrowsData.arrowsUpdateOn) { + const activeA = MixtrackPlatinumFX.deck[0].active ? 0 : 2; + const activeB = MixtrackPlatinumFX.deck[1].active ? 1 : 3; + + const bpmA = engine.getValue(`[Channel${ activeA+1 }]`, "bpm"); + const bpmB = engine.getValue(`[Channel${ activeB+1 }]`, "bpm"); + + let i; + for (i=0; i<4; i++) { + const bpmMy = engine.getValue(`[Channel${ i+1 }]`, "bpm"); + let bpmAlt = bpmA; + if (i===0 || i===2) { + bpmAlt = bpmB; + } + + let down=0; + let up=0; + + // only display if both decks have a bpm + if (bpmAlt && bpmMy) { + // and have a 0.05 bpm tolerance (else they only go off when you use sync) + if (bpmAlt>(bpmMy+0.05)) { + down=1; + } + if (bpmAlt<(bpmMy-0.05)) { + up=1; + } + } + + if (forceRefresh || MixtrackPlatinumFX.arrowsData.downarrow[i]!==down) { + MixtrackPlatinumFX.arrowsData.downarrow[i]=down; + midi.sendShortMsg(0x80 | i, 0x0A, down); // down arrow update + } + if (forceRefresh || MixtrackPlatinumFX.arrowsData.uparrow[i]!==up) { + MixtrackPlatinumFX.arrowsData.uparrow[i]=up; + midi.sendShortMsg(0x80 | i, 0x09, up); // up arrow update + } + } + } + } +}; + +MixtrackPlatinumFX.rateCallback = function(rate, group, _control) { + const channel = script.deckFromGroup(group) - 1; + const rateEffective = engine.getValue(group, "rateRange") * -rate; + + MixtrackPlatinumFX.sendScreenRateMidi(channel+1, Math.round(rateEffective*10000)); +}; + +MixtrackPlatinumFX.updateRateRange = function(channel, group, range) { + //engine.setParameter(group, "rateRange", (range-0.01)*0.25); + engine.setValue(group, "rateRange", range); + midi.sendShortMsg(0x90+channel, 0x0e, range*100); +};