Skip to content

Commit 97fb723

Browse files
committed
LPP 4-lane Sequencer first draft
1 parent b0321a4 commit 97fb723

File tree

4 files changed

+347
-159
lines changed

4 files changed

+347
-159
lines changed
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package com.novation.launchpadProMk3;
2+
3+
import com.bitwig.extension.controller.api.*;
4+
import com.bitwig.extensions.framework.Layer;
5+
import com.bitwig.extensions.framework.Layers;
6+
import com.bitwig.extensions.rh.StepViewPosition;
7+
8+
import java.util.Arrays;
9+
10+
public class DrumSeqMultilineLayer extends Layer {
11+
12+
private final PinnableCursorClip cursorClip;
13+
private final StepViewPosition positionHandler;
14+
private final DrumSequenceMode parent;
15+
private int drumScrollOffset;
16+
private int playingStep;
17+
boolean markIgnoreOrigLen;
18+
private LpColor trackColor;
19+
private final LpColor[] padColors = new LpColor[16];
20+
private final NoteStep[][] assignments = new NoteStep[4][32];
21+
private int currentPadIndex;
22+
23+
public DrumSeqMultilineLayer(final Layers layers, final LaunchpadProMk3ControllerExtension driver,
24+
DrumSequenceMode parent) {
25+
super(layers, "FOUR_X_SEQUENCER");
26+
this.parent = parent;
27+
ViewCursorControl control = driver.getViewControl();
28+
CursorTrack cursorTrack = control.getCursorTrack();
29+
cursorTrack.color().addValueObserver((r, g, b) -> trackColor = ColorLookup.getColor(r, g, b));
30+
cursorClip = cursorTrack.createLauncherCursorClip("4xCLIP", "4RowClip", 16, 4);
31+
final DrumPadBank drumPadBank = control.getDrumPadBank();
32+
Arrays.fill(padColors, LpColor.BLACK);
33+
drumPadBank.scrollPosition().addValueObserver(offset -> {
34+
drumScrollOffset = offset;
35+
cursorClip.scrollToKey(drumScrollOffset + currentPadIndex);
36+
});
37+
for (int i = 0; i < drumPadBank.getSizeOfBank(); i++) {
38+
final int index = i;
39+
DrumPad pad = drumPadBank.getItemAt(i);
40+
pad.color().addValueObserver((r, g, b) -> padColors[index] = ColorLookup.getColor(r, g, b, trackColor));
41+
}
42+
43+
initDrumPadButtons(driver.getHwElements());
44+
45+
cursorClip.addNoteStepObserver(this::handleNoteStep);
46+
cursorClip.playingStep().addValueObserver(this::handlePlayingStep);
47+
cursorClip.getLoopLength().addValueObserver(clipLength -> {
48+
if (markIgnoreOrigLen) {
49+
markIgnoreOrigLen = false;
50+
}
51+
});
52+
53+
positionHandler = new StepViewPosition(cursorClip);
54+
}
55+
56+
public void setSelectPadIndex(int padIndex) {
57+
this.currentPadIndex = Math.min(padIndex, 12);
58+
cursorClip.scrollToKey(drumScrollOffset + currentPadIndex);
59+
}
60+
61+
private void initDrumPadButtons(HardwareElements hwElements) {
62+
for (int row = 0; row < 8; row++) {
63+
for (int col = 0; col < 8; col++) {
64+
final GridButton button = hwElements.getGridButton(row, col);
65+
int noteIndex = row / 2;
66+
int stepIndex = (row % 2) * 8 + col;
67+
button.bindPressed(this, () -> handleSeqSelection(noteIndex, stepIndex));
68+
button.bindLight(this, () -> getColor(noteIndex, stepIndex));
69+
}
70+
}
71+
}
72+
73+
private RgbState getColor(int noteIndex, int stepIndex) {
74+
final int steps = positionHandler.getAvailableSteps();
75+
NoteStep assignment = assignments[noteIndex][stepIndex];
76+
LpColor color = padColors[(noteIndex + currentPadIndex) % 16];
77+
if (stepIndex < steps) {
78+
if (assignment == null || assignment.state() != NoteStep.State.NoteOn) {
79+
if (stepIndex == playingStep) {
80+
return RgbState.of(2);
81+
}
82+
return RgbState.of(1);
83+
}
84+
if (parent.isRandomModeActive()) {
85+
final double chance = assignment.chance();
86+
if (chance == 1) {
87+
if (stepIndex == playingStep) {
88+
return RgbState.of(color.getHiIndex());
89+
}
90+
return RgbState.of(color.getIndex());
91+
} else {
92+
final LpColor chanceColor = parent.toColor(chance);
93+
if (stepIndex == playingStep) {
94+
return RgbState.of(chanceColor.getHiIndex());
95+
}
96+
return RgbState.of(chanceColor.getIndex());
97+
}
98+
} else {
99+
if (stepIndex == playingStep) {
100+
return RgbState.of(color.getHiIndex());
101+
}
102+
return RgbState.of(color.getIndex());
103+
}
104+
}
105+
return RgbState.of(0);
106+
}
107+
108+
private void handleSeqSelection(final int noteIndex, final int stepIndex) {
109+
final NoteStep note = assignments[noteIndex][stepIndex];
110+
if (parent.isFixedLengthHeld()) {
111+
parent.stepActionFixedLength(stepIndex);
112+
} else if (parent.isRandomModeActive()) {
113+
stepActionRandomMode(stepIndex, note);
114+
} else {
115+
if (note == null || note.state() == NoteStep.State.Empty) {
116+
cursorClip.setStep(stepIndex, noteIndex, parent.getRefVelocity(),
117+
positionHandler.getGridResolution() * parent.getGatePercent());
118+
} else {
119+
cursorClip.toggleStep(stepIndex, noteIndex, parent.getRefVelocity());
120+
}
121+
}
122+
}
123+
124+
void stepActionRandomMode(final int index, final NoteStep note) {
125+
DrumSequenceMode.RndConfig rndConfig = parent.getCurrentRndValue();
126+
final double setProb = rndConfig.getProb();
127+
if (note != null && note.state() == NoteStep.State.NoteOn) {
128+
final double prob = note.chance();
129+
if (prob == setProb) {
130+
note.setChance(1);
131+
} else {
132+
note.setChance(setProb);
133+
}
134+
} else if (note == null || note.state() == NoteStep.State.Empty) {
135+
cursorClip.setStep(index, 0, parent.getRefVelocity(),
136+
positionHandler.getGridResolution() * parent.getGatePercent());
137+
//probValues[index] = RND_VALUES[selectedRndIndex].prob;
138+
}
139+
}
140+
141+
private void handleNoteStep(final NoteStep noteStep) {
142+
assignments[noteStep.y()][noteStep.x()] = noteStep;
143+
// if (probValues[noteStep.x()] != null && noteStep.state() == NoteStep.State.NoteOn) {
144+
// noteStep.setChance(probValues[noteStep.x()]);
145+
// probValues[noteStep.x()] = null;
146+
// }
147+
}
148+
149+
private void handlePlayingStep(final int playingStep) {
150+
if (playingStep == -1) {
151+
this.playingStep = -1;
152+
}
153+
this.playingStep = playingStep - positionHandler.getStepOffset();
154+
}
155+
156+
public void setGridResolution(double gridRate) {
157+
positionHandler.setGridResolution(gridRate);
158+
}
159+
}

src/main/java/com/novation/launchpadProMk3/DrumSequenceMode.java

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class DrumSequenceMode extends Layer {
2929
private static final LpColor[] ARP_BUTTON_COLORS = new LpColor[]{LpColor.BLUE, LpColor.BLUE, LpColor.BLUE, LpColor.BLUE, LpColor.PURPLE, LpColor.PURPLE, LpColor.PURPLE, LpColor.PURPLE};
3030
private static final LpColor[] GRID_BUTTON_COLORS = new LpColor[]{LpColor.PINK, LpColor.PINK, LpColor.PINK, LpColor.PINK, LpColor.PINK, LpColor.RED, LpColor.RED, LpColor.RED};
3131

32-
private static final RndConfig[] RND_VALUES = new RndConfig[]{RndConfig.P25, RndConfig.P50, RndConfig.P75};
32+
static final RndConfig[] RND_VALUES = new RndConfig[]{RndConfig.P25, RndConfig.P50, RndConfig.P75};
3333

3434
private int selectedRefVel = 0;
3535
private int selectedRndIndex = 2;
@@ -71,6 +71,7 @@ public class DrumSequenceMode extends Layer {
7171
private final Layer sendsLayer;
7272
private final Layer shiftLayer;
7373
private final Layer randomLayer;
74+
private final DrumSeqMultilineLayer multilineSeqLayer;
7475

7576
private Layer currentLayer;
7677

@@ -132,6 +133,7 @@ public PadContainer(final int index, final DrumPad pad, final BooleanValueObject
132133
selectedPad = this;
133134
focusOnSelectedPad();
134135
selectedPadIndex = index;
136+
multilineSeqLayer.setSelectPadIndex(index);
135137
}
136138
});
137139
pad.exists().addValueObserver(exists -> this.exists = exists);
@@ -220,6 +222,7 @@ public DrumSequenceMode(final Layers layers, final LaunchpadProMk3ControllerExte
220222
clipAreaNavLayer = new Layer(getLayers(), getName() + "_NAV");
221223
sendsLayer = new Layer(getLayers(), getName() + "_VEL");
222224
randomLayer = new Layer(getLayers(), getName() + "_RANDOM");
225+
multilineSeqLayer = new DrumSeqMultilineLayer(layers, driver, this);
223226
currentLayer = mainLayer;
224227

225228
noteInput = driver.getNoteInput();
@@ -305,6 +308,10 @@ private void initSpecialButtons(LaunchpadProMk3ControllerExtension driver) {
305308
hwElements.getButton(LabelCcAssignments.CUSTOM)
306309
.bindToggle(mainLayer, randomModeActive, LpColor.GREEN_SPRING, LpColor.BLACK);
307310
hwElements.getButton(LabelCcAssignments.STOP_CLIP_SWING).bindPressed(mainLayer, stopButtonHeld, LpColor.RED);
311+
hwElements.getButton(LabelCcAssignments.CHORD).bindPressed(mainLayer, multilineSeqLayer::toggleIsActive);
312+
hwElements.getButton(LabelCcAssignments.CHORD)
313+
.bindLight(mainLayer,
314+
() -> multilineSeqLayer.isActive() ? RgbState.of(LpColor.ORANGE) : RgbState.of(LpColor.BLACK));
308315

309316
final LabeledButton deviceButton = hwElements.getButton(LabelCcAssignments.DEVICE_TEMPO);
310317
final ViewCursorControl control = driver.getViewControl();
@@ -374,7 +381,8 @@ private void initDrumPadButtons(final LaunchpadProMk3ControllerExtension driver)
374381
final PadContainer pad = new PadContainer(index, control.getDrumPadBank().getItemAt(index),
375382
playing[index]);
376383
pads.add(pad);
377-
button.bindPressed(mainLayer, p -> handlePadSelection(pad, p), pad::getColor);
384+
button.bindPressed(mainLayer, () -> handlePadSelection(pad));
385+
button.bindLight(mainLayer, pad::getColor);
378386
button.bindToggle(muteLayer, pad.pad.mute());
379387
button.bindLight(muteLayer, pad::mutingColors);
380388
button.bindToggle(soloLayer, pad.pad.solo());
@@ -383,7 +391,7 @@ private void initDrumPadButtons(final LaunchpadProMk3ControllerExtension driver)
383391
button.bindLight(sendsLayer, pad::sendStatusColor);
384392
}
385393
}
386-
pads.sort((pc1, pc2) -> pc1.index - pc2.index);
394+
pads.sort(Comparator.comparingInt(pc -> pc.index));
387395
}
388396

389397
private void initSequenceSection(final LaunchpadProMk3ControllerExtension driver) {
@@ -493,6 +501,14 @@ private void adjustMode(final int notes) {
493501
}
494502
}
495503

504+
public double getGatePercent() {
505+
return gatePercent;
506+
}
507+
508+
public int getRefVelocity() {
509+
return velTable[selectedRefVel];
510+
}
511+
496512
public void setBackToOriginalLength() {
497513
adjustMode(originalClipLength);
498514
cursorClip.getLoopLength().set(originalClipLength);
@@ -535,6 +551,7 @@ private void assignGridResolution(final List<LabeledButton> sceneButtons) {
535551
button.bind(this, () -> {
536552
selectedGridIndex = index;
537553
positionHandler.setGridResolution(GRID_RATES[index]);
554+
multilineSeqLayer.setGridResolution(GRID_RATES[index]);
538555
}, () -> getGridState(index));
539556
}
540557
}
@@ -689,6 +706,14 @@ private void handlePlayingStep(final int playingStep) {
689706
this.playingStep = playingStep - positionHandler.getStepOffset();
690707
}
691708

709+
public boolean isFixedLengthHeld() {
710+
return fixedLengthHeld.get();
711+
}
712+
713+
public boolean isRandomModeActive() {
714+
return randomModeActive.get();
715+
}
716+
692717
private void handleSeqSelection(final int index, final boolean pressed) {
693718
if (!pressed) {
694719
return;
@@ -708,6 +733,10 @@ private void handleSeqSelection(final int index, final boolean pressed) {
708733
}
709734
}
710735

736+
public RndConfig getCurrentRndValue() {
737+
return RND_VALUES[selectedRndIndex];
738+
}
739+
711740
private void stepActionRandomMode(final int index, final NoteStep note) {
712741
final double setProb = RND_VALUES[selectedRndIndex].prob;
713742
if (note != null && note.state() == State.NoteOn) {
@@ -723,16 +752,13 @@ private void stepActionRandomMode(final int index, final NoteStep note) {
723752
}
724753
}
725754

726-
private void stepActionFixedLength(final int index) {
755+
void stepActionFixedLength(final int index) {
727756
final double newLen = positionHandler.lengthWithLastStep(index);
728757
adjustMode(newLen);
729758
cursorClip.getLoopLength().set(newLen);
730759
}
731760

732-
private void handlePadSelection(final PadContainer pad, final boolean pressed) {
733-
if (!pressed) {
734-
return;
735-
}
761+
private void handlePadSelection(final PadContainer pad) {
736762
if (states.getClearButtonPressed().get()) {
737763
cursorClip.scrollToKey(drumScrollOffset + pad.index);
738764
if (randomModeActive.get()) {
@@ -757,6 +783,7 @@ private void handlePadSelection(final PadContainer pad, final boolean pressed) {
757783
}
758784
} else {
759785
pad.pad.selectInEditor();
786+
multilineSeqLayer.setSelectPadIndex(pad.index);
760787
}
761788
}
762789

@@ -801,7 +828,7 @@ private RgbState stepState(final int index) {
801828
return RgbState.of(0);
802829
}
803830

804-
private LpColor toColor(final double chance) {
831+
LpColor toColor(final double chance) {
805832
if (chance == 0) {
806833
return LpColor.GREY_MD;
807834
}

src/main/java/com/novation/launchpadProMk3/LaunchPadProMk3ExtensionDefinition.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public String getAuthor() {
2525

2626
@Override
2727
public String getVersion() {
28-
return "1.01";
28+
return "1.02";
2929
}
3030

3131
@Override

0 commit comments

Comments
 (0)