diff --git a/res/controllers/Hercules P32 DJ.midi.xml b/res/controllers/Hercules P32 DJ.midi.xml index d9fd7f4b39e4..203ac3aff2c6 100644 --- a/res/controllers/Hercules P32 DJ.midi.xml +++ b/res/controllers/Hercules P32 DJ.midi.xml @@ -41,6 +41,78 @@ + + [Channel1] + P32.leftDeck.leftEncoder.input + 0xB1 + 0x0A + + + + + + [Channel1] + P32.leftDeck.leftEncoder.input + 0xB4 + 0x0A + + + + + + [Channel1] + P32.leftDeck.leftEncoderPress.input + 0x91 + 0x01 + + + + + + [Channel1] + P32.leftDeck.leftEncoderPress.input + 0x94 + 0x01 + + + + + + [Channel1] + P32.leftDeck.rightEncoder.input + 0xB1 + 0x05 + + + + + + [Channel1] + P32.leftDeck.rightEncoder.input + 0xB4 + 0x05 + + + + + + [Channel1] + P32.leftDeck.rightEncoderPress.input + 0x91 + 0x02 + + + + + + [Channel1] + P32.leftDeck.rightEncoderPress.input + 0x94 + 0x02 + + + + [Channel1] P32.leftDeck.loopIn.input @@ -61,7 +133,7 @@ [Channel1] - P32.leftDeck.loopTogglePad.input + P32.leftDeck.reloop.input 0x91 0x52 @@ -88,7 +160,7 @@ [Channel1] - P32.leftDeck.loopTogglePad.input + P32.leftDeck.reloop.input 0x94 0x52 @@ -485,7 +557,7 @@ [Channel1] - P32.leftDeck.effectUnit.showParametersButton.input + P32.leftDeck.effectUnit.effectFocusButton.input 0x91 0x06 @@ -521,7 +593,7 @@ [Channel1] - P32.leftDeck.effectUnit.showParametersButton.input + P32.leftDeck.effectUnit.effectFocusButton.input 0x94 0x06 @@ -530,279 +602,180 @@ [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Channel1.input + P32.leftDeck.play.input 0x91 - 0x40 - - - - - - [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Channel1.input - 0x94 - 0x40 + 0x0A [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Channel2.input + P32.leftDeck.cue.input 0x91 - 0x41 + 0x09 [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Channel2.input + P32.leftDeck.cue.input 0x94 - 0x41 - - - - - - [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Channel3.input - 0x91 - 0x42 + 0x09 [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Channel3.input + P32.leftDeck.play.input 0x94 - 0x42 + 0x0A [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Channel4.input + P32.leftDeck.sync.input 0x91 - 0x43 + 0x08 [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Channel4.input + P32.leftDeck.sync.input 0x94 - 0x43 - - - - - - [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Headphone.input - 0x91 - 0x34 - - - - - - [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Master.input - 0x91 - 0x35 - - - - - - [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Microphone.input - 0x91 - 0x36 + 0x08 [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Auxiliary1.input + P32.leftDeck.tempSlow.input 0x91 - 0x37 - - - - - - [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Headphone.input - 0x94 - 0x34 + 0x44 [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Master.input + P32.leftDeck.tempSlow.input 0x94 - 0x35 + 0x44 [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Microphone.input - 0x94 - 0x36 + P32.leftDeck.tempFast.input + 0x91 + 0x45 [Channel1] - P32.leftDeck.effectUnit.enableOnChannelButtons.Auxiliary1.input + P32.leftDeck.tempFast.input 0x94 - 0x37 - - - - - - [Channel1] - P32.leftDeck.loopMoveEncoder - 0xB4 - 0x0A - - - - - - [Channel1] - P32.leftDeck.play.input - 0x91 - 0x0A + 0x45 [Channel1] - P32.leftDeck.cue.input + P32.leftDeck.alignBeats.input 0x91 - 0x09 - - - - - - [Channel1] - P32.leftDeck.cue.input - 0x94 - 0x09 + 0x46 [Channel1] - P32.leftDeck.play.input + P32.leftDeck.alignBeats.input 0x94 - 0x0A + 0x46 [Channel1] - P32.leftDeck.sync.input + P32.leftDeck.enableEffectUnitButtons[0].input 0x91 - 0x08 + 0x40 [Channel1] - P32.leftDeck.sync.input + P32.leftDeck.enableEffectUnitButtons[0].input 0x94 - 0x08 + 0x40 [Channel1] - P32.leftDeck.tempSlow.input + P32.leftDeck.enableEffectUnitButtons[1].input 0x91 - 0x44 + 0x41 [Channel1] - P32.leftDeck.tempSlow.input + P32.leftDeck.enableEffectUnitButtons[1].input 0x94 - 0x44 - - - - - - [Channel1] - P32.leftDeck.tempFast.input - 0x91 - 0x45 + 0x41 [Channel1] - P32.leftDeck.tempFast.input + P32.leftDeck.enableEffectUnitButtons[2].input 0x94 - 0x45 + 0x3C [Channel1] - P32.leftDeck.alignBeats.input + P32.leftDeck.enableEffectUnitButtons[2].input 0x91 - 0x46 - - - - - - [Channel1] - P32.leftDeck.alignBeats.input - 0x94 - 0x46 + 0x3C [Channel1] - P32.leftDeck.quantize.input + P32.leftDeck.enableEffectUnitButtons[3].input 0x91 - 0x47 + 0x3D [Channel1] - P32.leftDeck.quantize.input + P32.leftDeck.enableEffectUnitButtons[3].input 0x94 - 0x47 + 0x3D @@ -816,15 +789,6 @@ - - [Channel1] - P32.leftDeck.tempoEncoder - 0xB1 - 0x05 - - - - [Channel1] P32.leftDeck.eqKnob[3].input @@ -852,15 +816,6 @@ - - [Channel1] - P32.leftDeck.beatJumpEncoder - 0xB4 - 0x05 - - - - [Playlist] P32.browseEncoder @@ -908,13 +863,22 @@ [Channel1] - P32.leftDeck.loadTrack + P32.leftDeck.loadTrack.input 0x91 0x0F + + [Channel1] + P32.leftDeck.loadTrack.input + 0x94 + 0x0F + + + + [Channel1] P32.leftDeck.volume.input @@ -943,63 +907,63 @@ - [Recording] - P32.slipButton.input - 0x90 - 0x03 + [Channel1] + P32.rightDeck.leftEncoder.input + 0xB2 + 0x05 [Channel1] - P32.leftDeck.tempoEncoderPress - 0x91 - 0x02 + P32.rightDeck.leftEncoder.input + 0xB5 + 0x05 [Channel1] - P32.leftDeck.ejectTrack - 0x94 - 0x0F + P32.rightDeck.leftEncoderPress.input + 0x92 + 0x02 [Channel1] - P32.leftDeck.volume.input - 0xB4 - 0x01 + P32.rightDeck.leftEncoderPress.input + 0x95 + 0x02 [Channel1] - P32.leftDeck.loopToggleEncoderPress - 0x91 - 0x01 + P32.rightDeck.rightEncoder.input + 0xB2 + 0x0A [Channel1] - P32.leftDeck.beatJumpEncoderPress - 0x94 - 0x02 + P32.rightDeck.rightEncoder.input + 0xB5 + 0x0A [Channel1] - P32.leftDeck.loopEncoderManualLoopPress - 0x94 + P32.rightDeck.rightEncoderPress.input + 0x92 0x01 @@ -1007,18 +971,27 @@ [Channel1] - P32.leftDeck.loopSizeEncoder.input - 0xB1 - 0x0A + P32.rightDeck.rightEncoderPress.input + 0x95 + 0x01 + + + + + + [Recording] + P32.slipButton.input + 0x90 + 0x03 [Channel1] - P32.rightDeck.loopMoveEncoder - 0xB5 - 0x0A + P32.leftDeck.volume.input + 0xB4 + 0x01 @@ -1072,43 +1045,25 @@ [Channel1] P32.rightDeck.sync.input 0x95 - 0x08 - - - - - - [Channel1] - P32.rightDeck.shiftButton - 0x92 - 0x07 - - - - - - [Channel1] - P32.rightDeck.tempoEncoder - 0xB2 - 0x05 + 0x08 [Channel1] - P32.rightDeck.eqKnob[3].input - 0xB2 - 0x04 + P32.rightDeck.shiftButton + 0x92 + 0x07 [Channel1] - P32.rightDeck.beatJumpEncoder - 0xB5 - 0x05 + P32.rightDeck.eqKnob[3].input + 0xB2 + 0x04 @@ -1169,7 +1124,7 @@ [Channel1] - P32.rightDeck.loadTrack + P32.rightDeck.loadTrack.input 0x92 0x0F @@ -1178,34 +1133,7 @@ [Channel1] - P32.rightDeck.volume.input - 0xB2 - 0x01 - - - - - - [Channel1] - P32.rightDeck.eqKnob[1].input - 0xB5 - 0x02 - - - - - - [Channel1] - P32.rightDeck.tempoEncoderPress - 0x92 - 0x02 - - - - - - [Channel1] - P32.rightDeck.ejectTrack + P32.rightDeck.loadTrack.input 0x95 0x0F @@ -1215,16 +1143,7 @@ [Channel1] P32.rightDeck.volume.input - 0xB5 - 0x01 - - - - - - [Channel1] - P32.rightDeck.loopToggleEncoderPress - 0x92 + 0xB2 0x01 @@ -1232,8 +1151,8 @@ [Channel1] - P32.rightDeck.beatJumpEncoderPress - 0x95 + P32.rightDeck.eqKnob[1].input + 0xB5 0x02 @@ -1241,22 +1160,13 @@ [Channel1] - P32.rightDeck.loopEncoderManualLoopPress - 0x95 + P32.rightDeck.volume.input + 0xB5 0x01 - - [Channel1] - P32.rightDeck.loopSizeEncoder.input - 0xB2 - 0x0A - - - - [Channel1] P32.rightDeck.hotcueButton[1].input @@ -1647,7 +1557,7 @@ [Channel1] - P32.rightDeck.effectUnit.showParametersButton.input + P32.rightDeck.effectUnit.effectFocusButton.input 0x92 0x06 @@ -1683,157 +1593,13 @@ [Channel1] - P32.rightDeck.effectUnit.showParametersButton.input + P32.rightDeck.effectUnit.effectFocusButton.input 0x95 0x06 - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Headphone.input - 0x92 - 0x34 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Master.input - 0x92 - 0x35 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Microphone.input - 0x92 - 0x36 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Auxiliary1.input - 0x92 - 0x37 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Headphone.input - 0x95 - 0x34 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Master.input - 0x95 - 0x35 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Microphone.input - 0x95 - 0x36 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Auxiliary1.input - 0x95 - 0x37 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Channel1.input - 0x92 - 0x40 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Channel1.input - 0x95 - 0x40 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Channel2.input - 0x92 - 0x41 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Channel2.input - 0x95 - 0x41 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Channel3.input - 0x92 - 0x42 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Channel3.input - 0x95 - 0x42 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Channel4.input - 0x92 - 0x43 - - - - - - [Channel1] - P32.rightDeck.effectUnit.enableOnChannelButtons.Channel4.input - 0x95 - 0x43 - - - - [Channel1] P32.leftDeck.samplerButton[1].input @@ -1872,7 +1638,7 @@ [Channel1] - P32.leftDeck.samplerButton[5].input + P32.leftDeck.samplerButton[9].input 0x91 0x2C @@ -1881,7 +1647,7 @@ [Channel1] - P32.leftDeck.samplerButton[6].input + P32.leftDeck.samplerButton[10].input 0x91 0x2D @@ -1890,7 +1656,7 @@ [Channel1] - P32.leftDeck.samplerButton[7].input + P32.leftDeck.samplerButton[11].input 0x91 0x2E @@ -1899,7 +1665,7 @@ [Channel1] - P32.leftDeck.samplerButton[8].input + P32.leftDeck.samplerButton[12].input 0x91 0x2F @@ -1908,7 +1674,7 @@ [Channel1] - P32.leftDeck.samplerButton[9].input + P32.leftDeck.samplerButton[17].input 0x91 0x28 @@ -1917,7 +1683,7 @@ [Channel1] - P32.leftDeck.samplerButton[10].input + P32.leftDeck.samplerButton[18].input 0x91 0x29 @@ -1926,7 +1692,7 @@ [Channel1] - P32.leftDeck.samplerButton[11].input + P32.leftDeck.samplerButton[19].input 0x91 0x2A @@ -1935,7 +1701,7 @@ [Channel1] - P32.leftDeck.samplerButton[12].input + P32.leftDeck.samplerButton[20].input 0x91 0x2B @@ -1944,7 +1710,7 @@ [Channel1] - P32.leftDeck.samplerButton[13].input + P32.leftDeck.samplerButton[25].input 0x91 0x24 @@ -1953,7 +1719,7 @@ [Channel1] - P32.leftDeck.samplerButton[14].input + P32.leftDeck.samplerButton[26].input 0x91 0x25 @@ -1962,7 +1728,7 @@ [Channel1] - P32.leftDeck.samplerButton[15].input + P32.leftDeck.samplerButton[27].input 0x91 0x26 @@ -1971,7 +1737,7 @@ [Channel1] - P32.leftDeck.samplerButton[16].input + P32.leftDeck.samplerButton[28].input 0x91 0x27 @@ -2016,7 +1782,7 @@ [Channel1] - P32.leftDeck.samplerButton[5].input + P32.leftDeck.samplerButton[9].input 0x94 0x2C @@ -2025,7 +1791,7 @@ [Channel1] - P32.leftDeck.samplerButton[6].input + P32.leftDeck.samplerButton[10].input 0x94 0x2D @@ -2034,7 +1800,7 @@ [Channel1] - P32.leftDeck.samplerButton[7].input + P32.leftDeck.samplerButton[11].input 0x94 0x2E @@ -2043,7 +1809,7 @@ [Channel1] - P32.leftDeck.samplerButton[8].input + P32.leftDeck.samplerButton[12].input 0x94 0x2F @@ -2052,7 +1818,7 @@ [Channel1] - P32.leftDeck.samplerButton[9].input + P32.leftDeck.samplerButton[17].input 0x94 0x28 @@ -2061,7 +1827,7 @@ [Channel1] - P32.leftDeck.samplerButton[10].input + P32.leftDeck.samplerButton[18].input 0x94 0x29 @@ -2070,7 +1836,7 @@ [Channel1] - P32.leftDeck.samplerButton[11].input + P32.leftDeck.samplerButton[19].input 0x94 0x2A @@ -2079,7 +1845,7 @@ [Channel1] - P32.leftDeck.samplerButton[12].input + P32.leftDeck.samplerButton[20].input 0x94 0x2B @@ -2088,7 +1854,7 @@ [Channel1] - P32.leftDeck.samplerButton[13].input + P32.leftDeck.samplerButton[25].input 0x94 0x24 @@ -2097,7 +1863,7 @@ [Channel1] - P32.leftDeck.samplerButton[14].input + P32.leftDeck.samplerButton[26].input 0x94 0x25 @@ -2106,7 +1872,7 @@ [Channel1] - P32.leftDeck.samplerButton[15].input + P32.leftDeck.samplerButton[27].input 0x94 0x26 @@ -2115,7 +1881,7 @@ [Channel1] - P32.leftDeck.samplerButton[16].input + P32.leftDeck.samplerButton[28].input 0x94 0x27 @@ -2124,7 +1890,7 @@ [Channel1] - P32.rightDeck.samplerButton[17].input + P32.rightDeck.samplerButton[5].input 0x92 0x30 @@ -2133,7 +1899,7 @@ [Channel1] - P32.rightDeck.samplerButton[18].input + P32.rightDeck.samplerButton[6].input 0x92 0x31 @@ -2142,7 +1908,7 @@ [Channel1] - P32.rightDeck.samplerButton[19].input + P32.rightDeck.samplerButton[7].input 0x92 0x32 @@ -2151,7 +1917,7 @@ [Channel1] - P32.rightDeck.samplerButton[20].input + P32.rightDeck.samplerButton[8].input 0x92 0x33 @@ -2160,7 +1926,7 @@ [Channel1] - P32.rightDeck.samplerButton[21].input + P32.rightDeck.samplerButton[13].input 0x92 0x2C @@ -2169,7 +1935,7 @@ [Channel1] - P32.rightDeck.samplerButton[22].input + P32.rightDeck.samplerButton[14].input 0x92 0x2D @@ -2178,7 +1944,7 @@ [Channel1] - P32.rightDeck.samplerButton[23].input + P32.rightDeck.samplerButton[15].input 0x92 0x2E @@ -2187,7 +1953,7 @@ [Channel1] - P32.rightDeck.samplerButton[24].input + P32.rightDeck.samplerButton[16].input 0x92 0x2F @@ -2196,7 +1962,7 @@ [Channel1] - P32.rightDeck.samplerButton[25].input + P32.rightDeck.samplerButton[21].input 0x92 0x28 @@ -2205,7 +1971,7 @@ [Channel1] - P32.rightDeck.samplerButton[26].input + P32.rightDeck.samplerButton[22].input 0x92 0x29 @@ -2214,7 +1980,7 @@ [Channel1] - P32.rightDeck.samplerButton[27].input + P32.rightDeck.samplerButton[23].input 0x92 0x2A @@ -2223,7 +1989,7 @@ [Channel1] - P32.rightDeck.samplerButton[28].input + P32.rightDeck.samplerButton[24].input 0x92 0x2B @@ -2268,7 +2034,7 @@ [Channel1] - P32.rightDeck.samplerButton[17].input + P32.rightDeck.samplerButton[5].input 0x95 0x30 @@ -2277,7 +2043,7 @@ [Channel1] - P32.rightDeck.samplerButton[18].input + P32.rightDeck.samplerButton[6].input 0x95 0x31 @@ -2286,7 +2052,7 @@ [Channel1] - P32.rightDeck.samplerButton[19].input + P32.rightDeck.samplerButton[7].input 0x95 0x32 @@ -2295,7 +2061,7 @@ [Channel1] - P32.rightDeck.samplerButton[20].input + P32.rightDeck.samplerButton[8].input 0x95 0x33 @@ -2304,7 +2070,7 @@ [Channel1] - P32.rightDeck.samplerButton[21].input + P32.rightDeck.samplerButton[13].input 0x95 0x2C @@ -2313,7 +2079,7 @@ [Channel1] - P32.rightDeck.samplerButton[22].input + P32.rightDeck.samplerButton[14].input 0x95 0x2D @@ -2322,7 +2088,7 @@ [Channel1] - P32.rightDeck.samplerButton[23].input + P32.rightDeck.samplerButton[15].input 0x95 0x2E @@ -2331,7 +2097,7 @@ [Channel1] - P32.rightDeck.samplerButton[24].input + P32.rightDeck.samplerButton[16].input 0x95 0x2F @@ -2340,7 +2106,7 @@ [Channel1] - P32.rightDeck.samplerButton[25].input + P32.rightDeck.samplerButton[21].input 0x95 0x28 @@ -2349,7 +2115,7 @@ [Channel1] - P32.rightDeck.samplerButton[26].input + P32.rightDeck.samplerButton[22].input 0x95 0x29 @@ -2358,7 +2124,7 @@ [Channel1] - P32.rightDeck.samplerButton[27].input + P32.rightDeck.samplerButton[23].input 0x95 0x2A @@ -2367,7 +2133,7 @@ [Channel1] - P32.rightDeck.samplerButton[28].input + P32.rightDeck.samplerButton[24].input 0x95 0x2B @@ -2430,7 +2196,7 @@ [Channel1] - P32.rightDeck.loopTogglePad.input + P32.rightDeck.reloop.input 0x92 0x52 @@ -2457,7 +2223,7 @@ [Channel1] - P32.rightDeck.loopTogglePad.input + P32.rightDeck.reloop.input 0x95 0x52 @@ -2520,18 +2286,72 @@ [Channel1] - P32.rightDeck.quantize.input + P32.rightDeck.enableEffectUnitButtons[0].input + 0x92 + 0x40 + + + + + + [Channel1] + P32.rightDeck.enableEffectUnitButtons[0].input + 0x95 + 0x40 + + + + + + [Channel1] + P32.rightDeck.enableEffectUnitButtons[1].input + 0x92 + 0x41 + + + + + + [Channel1] + P32.rightDeck.enableEffectUnitButtons[1].input + 0x95 + 0x41 + + + + + + [Channel1] + P32.rightDeck.enableEffectUnitButtons[2].input + 0x92 + 0x3C + + + + + + [Channel1] + P32.rightDeck.enableEffectUnitButtons[2].input + 0x95 + 0x3C + + + + + + [Channel1] + P32.rightDeck.enableEffectUnitButtons[3].input 0x92 - 0x47 + 0x3D [Channel1] - P32.rightDeck.quantize.input + P32.rightDeck.enableEffectUnitButtons[3].input 0x95 - 0x47 + 0x3D diff --git a/res/controllers/Hercules-P32-scripts.js b/res/controllers/Hercules-P32-scripts.js index 83973075ab7b..4d79963def23 100644 --- a/res/controllers/Hercules-P32-scripts.js +++ b/res/controllers/Hercules-P32-scripts.js @@ -1,8 +1,11 @@ // USER CONFIGURABLE OPTIONS -// loop size (in beats) when Mixxx starts -var defaultLoopSize = 8; -// beat jump size when Mixxx starts -var defaultBeatJumpSize = 4; +// The labels on the encoders are mirrored, but the rest of the controller +// is asymmetrical. If this is confusing for you to use, set this to "false" to swap the +// mapping of the encoders on the right deck so the whole controller is asymmetrical. +var mirroredEncoders = true; +// Set this to "false" to be able to set the loop and beatjump sizes above 64 beats +// to values that cannot be shown on the controller's LED display. +var clampLoopAndBeatJumpSize = true; // Set to "true" to use the dot on the loop size LED display to indicate // that a loop is active. This restricts loop sizes to 2-32 beats and // may be helpful if you never use loops less than 2 beats long. @@ -40,18 +43,6 @@ P32.init = function () { components.Component.prototype.shiftChannel = true; components.Button.prototype.sendShifted = true; - /** - The P32 has encoders for changing tempo, so the actual tempo getting out of sync with a hardware - fader and dealing with soft takeover in that situation is not an issue. So, make toggling master - sync the default unshifted behavior and momentary sync the shifted behavior. - **/ - components.SyncButton.prototype.unshift = function () { - this.inKey = 'sync_enabled'; - }; - components.SyncButton.prototype.shift = function () { - this.inKey = 'beatsync'; - }; - if (engine.getValue('[Master]', 'num_samplers') < 32) { engine.setValue('[Master]', 'num_samplers', 32); } @@ -69,6 +60,7 @@ P32.shutdown = function () { midi.sendShortMsg(0x90 + channel, button, 0); } } + // TODO: ask Hercules if it is possible to clear the loop size LEDs }; P32.shiftOffset = 3; @@ -106,21 +98,35 @@ P32.recordButton = new components.Button({ midi: [0x90, 0x02], group: '[Recording]', inKey: 'toggle_recording', - onlyOnPress: false, outKey: 'status', sendShifted: false, }); P32.slipButton = new components.Button({ midi: [0x90, 0x03], + pressedToToggleDeck: false, input: function (channel, control, value, status, group) { - if (P32.leftDeck.isShifted) { + if (P32.leftDeck.isShifted && value === 127) { + // PFL button is controlling effect unit assignment to headphones while + // shift is pressed, so switch it back to controlling PFL so + // reconnecting the output works. + P32.leftDeck.pfl.unshift(); P32.leftDeck.toggle(); - } else if (P32.rightDeck.isShifted) { + this.pressedToToggleDeck = true; + } else if (P32.rightDeck.isShifted && value === 127) { + // PFL button is controlling effect unit assignment to headphones while + // shift is pressed, so switch it back to controlling PFL so + // reconnecting the output works. + P32.rightDeck.pfl.unshift(); P32.rightDeck.toggle(); + this.pressedToToggleDeck = true; } else { - for (var i = 1; i <= 4; i++) { - script.toggleControl('[Channel' + i + ']', 'slip_enabled'); + if (this.pressedToToggleDeck && value === 0) { + this.pressedToToggleDeck = false; + } else { + for (var i = 1; i <= 4; i++) { + script.toggleControl('[Channel' + i + ']', 'slip_enabled'); + } } } }, @@ -149,8 +155,6 @@ P32.slipButton = new components.Button({ P32.Deck = function (deckNumbers, channel) { components.Deck.call(this, deckNumbers); - var loopSize = defaultLoopSize; - var beatJumpSize = defaultBeatJumpSize; var theDeck = this; this.shiftButton = function (channel, control, value, status, group) { @@ -161,36 +165,219 @@ P32.Deck = function (deckNumbers, channel) { } }; - // ===================================== TRANSPORT ========================================= - this.sync = new components.SyncButton([0x90 + channel, 0x08]); - this.cue = new components.CueButton([0x90 + channel, 0x09]); - this.play = new components.PlayButton([0x90 + channel, 0x0A]); + this.loadTrack = new components.Button({ + midi: [0x90 + channel, 0x0F], + unshift: function () { + this.inKey = 'LoadSelectedTrack'; + }, + shift: function () { + this.inKey = 'eject'; + }, + }); - // ===================================== MIXER ============================================== - this.eqKnob = []; - for (var k = 1; k <= 3; k++) { - this.eqKnob[k] = new components.Pot({ - midi: [0xB0 + channel, 0x02 + k], - group: '[EqualizerRack1_' + this.currentDeck + '_Effect1]', - inKey: 'parameter' + k, - }); - } + // =============================== ENCODERS ========================================= + this.loopEncoder = new components.Encoder({ + // NOTE: these are the MIDI bytes for the digit LEDs, not input from the encoder. + midi: [0xB0 + channel, 0x1B], + unshift: function () { + this.input = function (channel, control, value, status, group) { + var loopSize = engine.getValue(this.group, 'beatloop_size'); + if (loopEnabledDot) { + if (value > 64 && loopSize > 2) { // turn left + // Unfortunately, there is no way to show 1 with a dot on the + // loop size LED. + engine.setValue(this.group, 'beatloop_size', loopSize / 2); + } else if (value < 64 && loopSize < 32) { // turn right + // Mixxx supports loops longer than 32 beats, but there is no way + // to show 64 with a dot on the loop size LED. + engine.setValue(this.group, 'beatloop_size', loopSize * 2); + } + } else { + if (value > 64 && loopSize > 1/32) { // turn left + engine.setValue(this.group, 'beatloop_size', loopSize / 2); + } else if (value < 64) { // turn right + if (clampLoopAndBeatJumpSize) { + if (loopSize * 2 <= 64) { + engine.setValue(this.group, 'beatloop_size', loopSize * 2); + } + } else { + engine.setValue(this.group, 'beatloop_size', loopSize * 2); + } + } + } + }; + }, + shift: function () { + this.input = function (channel, control, value, status, group) { + var direction = (value > 64) ? 'backward' : 'forward'; + script.triggerControl(this.group, 'beatjump_1_' + direction); + }; + }, + connect: function () { + this.connections[0] = engine.connectControl(this.group, 'beatloop_size', this.output); + if (loopEnabledDot) { + this.connections[1] = engine.connectControl(this.group, 'loop_enabled', this.output); + } + }, + output: function (value, group, control) { + var loopSize = engine.getValue(this.group, 'beatloop_size'); + var loopSizeLogBase2 = Math.log(loopSize) / Math.log(2); + // test if loopSizeLogBase2 is an integer + if (Math.floor(loopSizeLogBase2) === loopSizeLogBase2) { + if (loopEnabledDot && engine.getValue(this.group, 'loop_enabled') === 1) { + this.send(5 - loopSizeLogBase2); + } else { + this.send(5 + loopSizeLogBase2); + } + } else { + this.send(14); // show two dots + } + } + }); - this.pfl = new components.Button({ - midi: [0x90 + channel, 0x10], - key: 'pfl', - sendShifted: false, + this.loopEncoderPress = new components.Button({ + unshift: function () { + // Make sure the shifted Controls don't get stuck with a value of 1 + // if the shift button is released before the encoder button. + if (engine.getValue(this.group, 'reloop_andstop') !== 0) { + engine.setValue(this.group, 'reloop_andstop', 0); + } + if (engine.getValue(this.group, 'reloop_toggle') !== 0) { + engine.setValue(this.group, 'reloop_toggle', 0); + } + + this.input = function (channel, control, value, status, group) { + if (value) { + if (engine.getValue(this.group, 'loop_enabled') === 1) { + engine.setValue(this.group, 'reloop_toggle', 1); + } else { + engine.setValue(this.group, 'beatloop_activate', 1); + } + } else { + if (engine.getValue(this.group, 'reloop_toggle') !== 1) { + engine.setValue(this.group, 'reloop_toggle', 0); + } else if (engine.getValue(this.group, 'beatloop_activate') !== 0) { + engine.setValue(this.group, 'beatloop_activate', 0); + } + } + }; + }, + shift: function () { + // Make sure the unshifted Controls don't get stuck with a value of 1 + // if the shift button is pressed before releasing the encoder button. + if (engine.getValue(this.group, 'reloop_toggle') !== 0) { + engine.setValue(this.group, 'reloop_toggle', 0); + } + if (engine.getValue(this.group, 'beatloop_activate') !== 0) { + engine.setValue(this.group, 'beatloop_activate', 0); + } + + this.input = function (channel, control, value, status, group) { + if (engine.getValue(this.group, 'loop_enabled') === 1) { + engine.setValue(this.group, 'reloop_andstop', value / 127); + } else { + engine.setValue(this.group, 'reloop_toggle', value / 127); + } + }; + }, }); - this.volume = new components.Pot({ - midi: [0xB0 + channel, 0x01], - inKey: 'volume', + this.showBeatjumpSize = function () { + var beatjumpSize = engine.getValue(this.currentDeck, 'beatjump_size'); + var beatjumpSizeLogBase2 = Math.log(beatjumpSize) / Math.log(2); + // test if beatjumpSizeLogBase2 is an integer + if (Math.floor(beatjumpSizeLogBase2) === beatjumpSizeLogBase2) { + midi.sendShortMsg(0xB0 + channel, 0x1B, + 5 + Math.log(beatjumpSize) / Math.log(2)); + } else { + midi.sendShortMsg(0xB0 + channel, 0x1B, 14); // show two dots + } + }; + + this.tempoAndBeatjumpEncoder = new components.Encoder({ + unshift: function () { + this.input = function (channel, control, value, status, group) { + var direction = (value > 64) ? -1 : 1; + engine.setValue(this.group, 'rate', + engine.getValue(this.group, 'rate') + (0.01 * direction)); + }; + }, + shift: function () { + this.input = function (channel, control, value, status, group) { + var beatJumpSize = engine.getValue(this.group, 'beatjump_size'); + if (theDeck.beatJumpEncoderPressed) { + if (value > 64 && beatJumpSize > 1/32) { // turn left + beatJumpSize /= 2; + } else if (value < 64) { // turn right + if (clampLoopAndBeatJumpSize && beatJumpSize >= 64) { + return; + } + beatJumpSize *= 2; + } + engine.setValue(this.group, 'beatjump_size', beatJumpSize); + theDeck.showBeatjumpSize(); + } else { + var direction = (value > 64) ? 'backward' : 'forward'; + script.triggerControl(this.group, 'beatjump_' + direction); + } + }; + }, }); - // ==================================== PAD GRID ============================================ - // The slicer layer is handled by this.effectUnit.enableOnChannelButtons, set up under the - // EFFECTS section. + this.tempoAndBeatjumpEncoderPress = new components.Button({ + unshift: function () { + theDeck.loopEncoder.trigger(); + this.input = function (channel, control, value, status, group) { + if (value === 127) { + engine.setValue(this.group, 'rate', 0); + } + }; + }, + shift: function () { + this.input = function (channel, control, value, status, group) { + if (value === 127) { + theDeck.beatJumpEncoderPressed = true; + theDeck.showBeatjumpSize(); + } else { + theDeck.beatJumpEncoderPressed = false; + theDeck.loopEncoder.trigger(); + } + }; + }, + }); + if (mirroredEncoders) { + if (channel == 1) { // left deck + this.leftEncoder = this.loopEncoder; + this.leftEncoderPress = this.loopEncoderPress; + this.rightEncoder = this.tempoAndBeatjumpEncoder; + this.rightEncoderPress = this.tempoAndBeatjumpEncoderPress; + } else if (channel == 2) { // right deck + this.leftEncoder = this.tempoAndBeatjumpEncoder; + this.leftEncoderPress = this.tempoAndBeatjumpEncoderPress; + this.rightEncoder = this.loopEncoder; + this.rightEncoderPress = this.loopEncoderPress; + } + } else { + this.leftEncoder = this.loopEncoder; + this.leftEncoderPress = this.loopEncoderPress; + this.rightEncoder = this.tempoAndBeatjumpEncoder; + this.rightEncoderPress = this.tempoAndBeatjumpEncoderPress; + } + + // ================================= EFFECTS ===================================== + this.effectUnit = new components.EffectUnit(deckNumbers); + this.effectUnit.knobs[1].midi = [0xB0 + channel, 0x06]; + this.effectUnit.knobs[2].midi = [0xB0 + channel, 0x07]; + this.effectUnit.knobs[3].midi = [0xB0 + channel, 0x08]; + this.effectUnit.dryWetKnob.midi = [0xB0 + channel, 0x09]; + this.effectUnit.enableButtons[1].midi = [0x90 + channel, 0x03]; + this.effectUnit.enableButtons[2].midi = [0x90 + channel, 0x04]; + this.effectUnit.enableButtons[3].midi = [0x90 + channel, 0x05]; + this.effectUnit.effectFocusButton.midi = [0x90 + channel, 0x06]; + this.effectUnit.init(); + + // ================================ PAD GRID ==================================== this.hotcueButton = []; this.samplerButton = []; for (var i = 1; i <= 16; i++) { @@ -200,13 +387,18 @@ P32.Deck = function (deckNumbers, channel) { number: i, on: P32.padColors.red }); - var samplerNumber = i + (channel - 1) * 16; + + var row = Math.ceil(i/4); + var column = ((i-1) % 4) + 1; + var padGrid = channel - 1; + var samplerNumber = (8 * (row-1)) + (column) + (padGrid * 4); this.samplerButton[samplerNumber] = new components.SamplerButton({ midi: [0x90 + channel, P32.PadNumToMIDIControl(i, 0)], number: samplerNumber, - on: P32.padColors.red, - off: P32.padColors.off, - playing: P32.padColors.blue + empty: P32.padColors.off, + loaded: P32.padColors.red, + playing: P32.padColors.blue, + looping: P32.padColors.purple, }); if (samplerCrossfaderAssign) { engine.setValue('[Sampler' + samplerNumber + ']', @@ -216,212 +408,103 @@ P32.Deck = function (deckNumbers, channel) { } } + // LOOP layer this.loopIn = new components.Button({ midi: [0x90 + channel, 0x50], - inKey: 'loop_in', + key: 'loop_in', + on: P32.padColors.red, + off: P32.padColors.purple, }); this.loopOut = new components.Button({ midi: [0x90 + channel, 0x51], - inKey: 'loop_out', + key: 'loop_out', + on: P32.padColors.red, + off: P32.padColors.purple, }); - this.loopTogglePad = new components.LoopToggleButton({ + this.reloop = new components.Button({ midi: [0x90 + channel, 0x52], + unshift: function () { + this.inKey = 'reloop_toggle'; + }, + shift: function () { + this.inKey = 'reloop_andstop'; + }, on: P32.padColors.red, off: P32.padColors.blue, + outKey: 'loop_enabled', }); - this.loopIn.send(P32.padColors.purple); - this.loopOut.send(P32.padColors.purple); this.tempSlow = new components.Button({ midi: [0x90 + channel, 0x44], - inKey: 'rate_temp_down', - onlyOnPress: false, + key: 'rate_temp_down', + on: P32.padColors.red, + off: P32.padColors.purple, }); this.tempFast = new components.Button({ midi: [0x90 + channel, 0x45], - inKey: 'rate_temp_down', - onlyOnPress: false, + key: 'rate_temp_up', + on: P32.padColors.red, + off: P32.padColors.purple, }); this.alignBeats = new components.Button({ midi: [0x90 + channel, 0x46], - inKey: 'beats_translate_curpos', - }); - this.quantize = new components.Button({ - midi: [0x90 + channel, 0x47], - key: 'quantize', + key: 'beats_translate_curpos', on: P32.padColors.red, off: P32.padColors.blue, }); - this.tempSlow.send(P32.padColors.purple); - this.tempFast.send(P32.padColors.purple); - this.alignBeats.send(P32.padColors.blue); - - // =================================== ENCODERS ============================================== - this.loopSizeEncoder = new components.Encoder({ - midi: [0xB0 + channel, 0x1B], // Note: these are the MIDI bytes for the LED readout, not - // input from the encoder. - input: function (channel, control, value, status, group) { - if (loopEnabledDot) { - if (value > 64 && loopSize > 2) { // turn left - /** - Unfortunately, there is no way to show 1 with a dot on the - loop size LED. - **/ - loopSize /= 2; - engine.setValue(this.group, 'loop_halve', 1); - engine.setValue(this.group, 'loop_halve', 0); - } else if (value < 64 && loopSize < 32) { // turn right - /** - Mixxx supports loops longer than 32 beats, but there is no way - to show 64 with a dot on the loop size LED. - **/ - loopSize *= 2; - engine.setValue(this.group, 'loop_double', 1); - engine.setValue(this.group, 'loop_double', 0); - } - } else { - if (value > 64 && loopSize > 1/32) { // turn left - /** - Mixxx supports loops shorter than 1/32 beats, but there is no - way to set the loop size LED less than 1/32 (even though it - should be able to show 1/64) - **/ - loopSize /= 2; - engine.setValue(this.group, 'loop_halve', 1); - engine.setValue(this.group, 'loop_halve', 0); - } else if (value < 64 && loopSize < 64) { // turn right - /** - Mixxx supports loops longer than 64 beats, but the loop size LED - only has 2 digits, so it couldn't show 128 - **/ - loopSize *= 2; - engine.setValue(this.group, 'loop_double', 1); - engine.setValue(this.group, 'loop_double', 0); - } - } - this.trigger(); - }, - outKey: 'loop_enabled', - output: function (value, group, control) { - if (loopEnabledDot && value) { - this.send(5 - Math.log(loopSize) / Math.log(2)); - } else { - this.send(5 + Math.log(loopSize) / Math.log(2)); - } - } - }); - this.loopMoveEncoder = function (channel, control, value, status, group) { - var direction = (value > 64) ? -1 : 1; - if (loopSize < 1) { - engine.setValue(this.currentDeck, 'loop_move', loopSize * direction); - } else { - engine.setValue(this.currentDeck, 'loop_move', 1 * direction); + // SLICER layer + this.enableEffectUnitButtons = [0x40, 0x41, 0x3C, 0x3D].map( + function (midiByte, index) { + return new components.EffectAssignmentButton({ + midi: [0x90 + channel, midiByte], + effectUnit: index + 1, + group: this.currentDeck, + on: P32.padColors.blue, + off: P32.padColors.red, + }); } - }; + , this); - this.loopToggleEncoderPress = function (channel, control, value, status, group) { - if (value) { - if (engine.getValue(this.currentDeck, 'loop_enabled')) { - engine.setValue(this.currentDeck, 'reloop_exit', 1); - } else { - engine.setValue(this.currentDeck, 'beatloop_' + loopSize + '_activate', 1); - } - } else { - if (loopSize <= 1 && engine.getValue(this.currentDeck, 'loop_enabled')) { - engine.setValue(this.currentDeck, 'reloop_exit', 1); - } - } - }; - - this.loopEncoderManualLoopPress = function (channel, control, value, status, group) { - if (value) { - engine.setValue(this.currentDeck, 'loop_in', 1); - } else { - engine.setValue(this.currentDeck, 'loop_out', 1); - } - }; - - this.tempoEncoder = function (channel, control, value, status, group) { - var direction = (value > 64) ? -1 : 1; - engine.setValue(this.currentDeck, 'rate', engine.getValue(this.currentDeck, 'rate') + (0.01 * direction)); - }; - - this.tempoEncoderPress = function (channel, control, value, status, group) { - if (value) { - engine.setValue(this.currentDeck, 'rate', 0); - } - }; - - this.beatJumpEncoder = function (channel, control, value, status, group) { - var direction = (value > 64) ? -1 : 1; - if (this.beatJumpEncoderPressed) { - if (value > 64 && beatJumpSize > 1/32) { // turn left - beatJumpSize /= 2; - } else if (value < 64 && beatJumpSize < 64) { // turn right - beatJumpSize *= 2; - } - // The firmware will only change the numeric LED readout when sent messages - // on the unshifted channel. - midi.sendShortMsg(0xB0 + channel - P32.shiftOffset, 0x1B, 5 + Math.log(beatJumpSize) / Math.log(2)); - } else { - engine.setValue(this.currentDeck, 'beatjump', direction * beatJumpSize); - } - }; + // ============================= TRANSPORT ================================== + this.sync = new components.SyncButton([0x90 + channel, 0x08]); + this.cue = new components.CueButton([0x90 + channel, 0x09]); + this.play = new components.PlayButton([0x90 + channel, 0x0A]); - this.beatJumpEncoderPress = function (channel, control, value, status, group) { - // The firmware will only change the numeric LED readout when sent messages - // on the unshifted channel. - if (value === 127) { - this.beatJumpEncoderPressed = true; - midi.sendShortMsg(0xB0 + channel - P32.shiftOffset, 0x1B, 5 + Math.log(beatJumpSize) / Math.log(2)); - } else { - this.beatJumpEncoderPressed = false; - midi.sendShortMsg(0xB0 + channel - P32.shiftOffset, 0x1B, 5 + Math.log(loopSize) / Math.log(2)); - } - }; + // =============================== MIXER ==================================== + this.eqKnob = []; + for (var k = 1; k <= 3; k++) { + this.eqKnob[k] = new components.Pot({ + midi: [0xB0 + channel, 0x02 + k], + group: '[EqualizerRack1_' + this.currentDeck + '_Effect1]', + inKey: 'parameter' + k, + }); + } - this.loadTrack = function (channel, control, value, status, group) { - if (value === 127) { - engine.setValue(this.currentDeck, 'LoadSelectedTrack', 1); - } - }; + this.pfl = new components.Button({ + midi: [0x90 + channel, 0x10], + sendShifted: false, + type: components.Button.prototype.types.toggle, + unshift: function () { + this.group = theDeck.currentDeck; + this.inKey = 'pfl'; + }, + outKey: 'pfl', + shift: function () { + this.group = '[EffectRack1_EffectUnit' + theDeck.effectUnit.currentUnitNumber + ']'; + this.inKey = 'group_[Headphone]_enable'; + }, + }); - this.ejectTrack = function (channel, control, value, status, group) { - if (value === 127) { - engine.setValue(this.currentDeck, 'eject', 1); - engine.beginTimer(250, 'engine.setValue("'+this.currentDeck+'", "eject", 0)', true); - } - }; + this.volume = new components.Pot({ + midi: [0xB0 + channel, 0x01], + inKey: 'volume', + }); this.reconnectComponents(function (component) { if (component.group === undefined) { component.group = this.currentDeck; } }); - - // ==================================== EFFECTS ============================================== - this.effectUnit = new components.EffectUnit(deckNumbers[0]); - this.effectUnit.knobs[1].midi = [0xB0 + channel, 0x06]; - this.effectUnit.knobs[2].midi = [0xB0 + channel, 0x07]; - this.effectUnit.knobs[3].midi = [0xB0 + channel, 0x08]; - this.effectUnit.dryWetKnob.midi = [0xB0 + channel, 0x09]; - this.effectUnit.enableButtons[1].midi = [0x90 + channel, 0x03]; - this.effectUnit.enableButtons[2].midi = [0x90 + channel, 0x04]; - this.effectUnit.enableButtons[3].midi = [0x90 + channel, 0x05]; - this.effectUnit.showParametersButton.midi = [0x90 + channel, 0x06]; - this.effectUnit.enableOnChannelButtons.Channel1.midi = [0x90 + channel, 0x40]; - this.effectUnit.enableOnChannelButtons.Channel2.midi = [0x90 + channel, 0x41]; - this.effectUnit.enableOnChannelButtons.Channel3.midi = [0x90 + channel, 0x42]; - this.effectUnit.enableOnChannelButtons.Channel4.midi = [0x90 + channel, 0x43]; - this.effectUnit.enableOnChannelButtons.Headphone.midi = [0x90 + channel, 0x34]; - this.effectUnit.enableOnChannelButtons.Master.midi = [0x90 + channel, 0x35]; - this.effectUnit.enableOnChannelButtons.Microphone.midi = [0x90 + channel, 0x36]; - this.effectUnit.enableOnChannelButtons.Auxiliary1.midi = [0x90 + channel, 0x37]; - this.effectUnit.enableOnChannelButtons.forEachComponent(function (button) { - button.on = P32.padColors.red; - button.off = P32.padColors.blue; - }); - this.effectUnit.init(); }; P32.Deck.prototype = new components.Deck(); diff --git a/res/controllers/common-controller-scripts.js b/res/controllers/common-controller-scripts.js index cce9e53641aa..816ebb9bc9ed 100644 --- a/res/controllers/common-controller-scripts.js +++ b/res/controllers/common-controller-scripts.js @@ -126,6 +126,25 @@ script.toggleControl = function (group, control) { engine.setValue(group, control, !(engine.getValue(group, control))); } +/* -------- ------------------------------------------------------ + script.toggleControl + Purpose: Triggers an engine value and resets it back to 0 after a delay + This is helpful for mapping encoder turns to controls that are + represented by buttons in skins so the skin button lights up + briefly but does not stay lit. + Input: Group and control names, delay in milliseconds (optional) + Output: none + -------- ------------------------------------------------------ */ +script.triggerControl = function (group, control, delay) { + if (typeof delay !== 'number') { + delay = 200; + } + engine.setValue(group, control, 1); + engine.beginTimer(delay, function () { + engine.setValue(group, control, 0); + }, true); +} + /* -------- ------------------------------------------------------ script.absoluteLin Purpose: Maps an absolute linear control value to a linear Mixxx control diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 1215948e267d..1105a5b72a56 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -320,22 +320,35 @@ } else { engine.setValue(this.group, 'eject', 1); } + } else { + if (engine.getValue(this.group, 'play') === 0) { + engine.setValue(this.group, 'eject', 0); + } } }; }, output: function (value, group, control) { if (engine.getValue(this.group, 'track_loaded') === 1) { - if (this.playing === undefined) { + if (this.loaded === undefined) { this.send(this.on); } else { if (engine.getValue(this.group, 'play') === 1) { - this.send(this.on); + if (this.looping !== undefined && + engine.getValue(this.group, 'repeat') === 1) { + this.send(this.looping); + } else { + this.send(this.playing); + } } else { - this.send(this.playing); + this.send(this.loaded); } } } else { - this.send(this.off); + if (this.empty === undefined) { + this.send(this.off); + } else { + this.send(this.empty); + } } }, connect: function() { @@ -343,6 +356,9 @@ if (this.playing !== undefined) { this.connections[1] = engine.connectControl(this.group, 'play', this.output); } + if (this.looping !== undefined) { + this.connections[2] = engine.connectControl(this.group, 'repeat', this.output); + } }, outKey: null, // hack to get Component constructor to call connect() }); @@ -460,12 +476,22 @@ isShifted: false, shift: function () { this.forEachComponent(function (component) { + // Controls for push type Buttons depend on getting reset to 0 when the + // Button is released for correct behavior. If there is a skin button + // that lights up with the inKey, the skin button would stay lit if the + // inKey does not get reset to 0. So, if a push type Button is held down + // when shift is pressed, when the Button is released, the MIDI signal + // for the Button release will be processed when the Button is in the + // shifted state, and the unshifted inKey would not get reset to 0. + // To work around this, reset push Buttons' inKey to 0 when the shift + // button is pressed. if (typeof component.shift === 'function') { if (component instanceof Button && (component.type === Button.prototype.types.push || component.type === undefined) - && component.inKey !== undefined - && component.input === Button.prototype.input) { + && component.input === Button.prototype.input + && typeof component.inKey === 'string' + && typeof component.group === 'string') { if (engine.getValue(component.group, component.inKey) !== 0) { engine.setValue(component.group, component.inKey, 0); } @@ -478,12 +504,14 @@ }, unshift: function () { this.forEachComponent(function (component) { + // Refer to comment in ComponentContainer.shift() above for explanation if (typeof component.unshift === 'function') { if (component instanceof Button && (component.type === Button.prototype.types.push || component.type === undefined) - && component.inKey !== undefined - && component.input === Button.prototype.input) { + && component.input === Button.prototype.input + && typeof component.inKey === 'string' + && typeof component.group === 'string') { if (engine.getValue(component.group, component.inKey) !== 0) { engine.setValue(component.group, component.inKey, 0); }