Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,91 +1,91 @@
package com.bitwig.extensions.controllers.akai.apc64;

public class OverviewGrid {

public class ApcOverviewGrid {
private int sceneOffset;
private int trackOffset;
private int numberOfScenes;
private int numberOfTracks;

private int trackPosition;
private int scenePosition;

private final int[][] hasClips = new int[8][8];
private final int[] sceneQueuedClips = new int[64];

public int getNumberOfScenes() {
return numberOfScenes;
}

public void setNumberOfScenes(final int numberOfScenes) {
this.numberOfScenes = numberOfScenes;
}

public int getNumberOfTracks() {
return numberOfTracks;
}

public void setNumberOfTracks(final int numberOfTracks) {
this.numberOfTracks = numberOfTracks;
}

public int getTrackPosition() {
return trackPosition - trackOffset;
}

public int getTrackOffset() {
return trackOffset;
}

public void setTrackPosition(final int trackPosition) {
this.trackPosition = trackPosition;
this.trackOffset = (trackPosition / 64) * 64;
}

public int getScenePosition() {
return scenePosition - sceneOffset;
}

public void setScenePosition(final int scenePosition) {
this.scenePosition = scenePosition;
this.sceneOffset = (scenePosition / 64) * 64;
}

public int getSceneOffset() {
return sceneOffset;
}

public void markSceneQueued(int sceneIndex, boolean isQueued) {
public void markSceneQueued(final int sceneIndex, final boolean isQueued) {
if (isQueued) {
sceneQueuedClips[sceneIndex]++;
} else if (sceneQueuedClips[sceneIndex] > 0) {
sceneQueuedClips[sceneIndex]--;
}
}

public void setHasClips(int trackIndex, int sceneIndex, boolean hasClip) {
int gridScene = (sceneIndex) / 8;
int gridTrack = (trackIndex) / 8;
public void setHasClips(final int trackIndex, final int sceneIndex, final boolean hasClip) {
final int gridScene = (sceneIndex) / 8;
final int gridTrack = (trackIndex) / 8;
if (hasClip) {
this.hasClips[gridTrack][gridScene]++;
} else if (this.hasClips[gridTrack][gridScene] > 0) {
this.hasClips[gridTrack][gridScene]--;
}
}

public boolean hasClips(int trackIndex, int sceneIndex) {
public boolean hasClips(final int trackIndex, final int sceneIndex) {
return this.hasClips[trackIndex][sceneIndex] > 0;
}

public boolean hasQueuedScenes(int sceneIndex) {
int index = sceneIndex - sceneOffset;
public boolean hasQueuedScenes(final int sceneIndex) {
final int index = sceneIndex - sceneOffset;
if (index > 63) {
return false;
}
return this.sceneQueuedClips[sceneIndex - sceneOffset] > 0;
}

public boolean inGrid(int trackIndex, int sceneIndex) {
public boolean inGrid(final int trackIndex, final int sceneIndex) {
final int posX = trackIndex * 8;
final int posY = sceneIndex * 8;
return posX < (numberOfTracks - trackOffset) && posY < (numberOfScenes - sceneOffset);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,49 @@
package com.bitwig.extensions.controllers.akai.apc64;

import com.bitwig.extension.controller.api.*;
import com.bitwig.extensions.controllers.akai.apc.common.MidiProcessor;
import com.bitwig.extensions.framework.di.Component;
import com.bitwig.extensions.framework.di.Inject;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;

import com.bitwig.extension.controller.api.Application;
import com.bitwig.extension.controller.api.Clip;
import com.bitwig.extension.controller.api.ClipLauncherSlot;
import com.bitwig.extension.controller.api.ClipLauncherSlotBank;
import com.bitwig.extension.controller.api.ControllerHost;
import com.bitwig.extension.controller.api.CursorTrack;
import com.bitwig.extension.controller.api.Project;
import com.bitwig.extension.controller.api.Transport;
import com.bitwig.extensions.controllers.akai.apc.common.MidiProcessor;
import com.bitwig.extensions.framework.di.Component;
import com.bitwig.extensions.framework.di.Inject;

@Component
public class FocusClip {
private static final int SINGLE_SLOT_RANGE = 8;

private final CursorTrack cursorTrack;
private final Application application;
private final Transport transport;
private final Clip mainCursorClip;
private final Project project;
private final ControllerHost host;
private final OverviewGrid overviewGrid;

private final ApcOverviewGrid overviewGrid;
private int selectedSlotIndex = -1;
private int scrollOffset = 0;

private String currentTrackName = "";

private final Map<String, Integer> indexMemory = new HashMap<>();
private final ClipLauncherSlotBank slotBank;
private ClipLauncherSlot focusSlot;
private Runnable scrollTask = null;

@Inject
private MidiProcessor midiProcessor;

public FocusClip(ControllerHost host, Application application, Transport transport, ViewControl viewControl,
Project project) {
public FocusClip(final ControllerHost host, final Application application, final Transport transport,
final ViewControl viewControl, final Project project) {
this.cursorTrack = viewControl.getCursorTrack();
this.project = project;
this.host = host;
Expand All @@ -49,10 +56,10 @@ public FocusClip(ControllerHost host, Application application, Transport transpo
slot.isPlaying().markInterested();
slot.hasContent().markInterested();
}

this.application = application;
this.transport = transport;

slotBank.addPlaybackStateObserver((slotIndex, playbackState, isQueued) -> {
if (playbackState != 0 && !isQueued) {
slotBank.select(slotIndex);
Expand All @@ -73,7 +80,7 @@ public FocusClip(ControllerHost host, Application application, Transport transpo
scrollTask = null;
}
});

this.cursorTrack.name().addValueObserver(name -> {
selectedSlotIndex = -1;
currentTrackName = name;
Expand All @@ -84,71 +91,73 @@ public FocusClip(ControllerHost host, Application application, Transport transpo
});
mainCursorClip = viewControl.getCursorClip();
}

public void invokeRecord() {
if (selectedSlotIndex != -1) {
final ClipLauncherSlot slot = slotBank.getItemAt(selectedSlotIndex);
if (slot.isRecording().get()) {
slot.launch();
transport.isClipLauncherOverdubEnabled().set(false);
} else {
Optional<ClipLauncherSlot> emptySlot = getFirstEmptySlot(selectedSlotIndex);
final Optional<ClipLauncherSlot> emptySlot = getFirstEmptySlot(selectedSlotIndex);
if (emptySlot.isPresent()) {
recordAction(emptySlot.get());
} else {
project.createScene();
host.scheduleTask(
() -> getFirstEmptySlot(selectedSlotIndex).ifPresent(newSlot -> recordAction(newSlot)), 50);
() -> getFirstEmptySlot(selectedSlotIndex).ifPresent(newSlot -> recordAction(newSlot)), 50);
}
}
} else {
getFirstEmptySlot(selectedSlotIndex).ifPresent(slot -> recordAction(slot));
}
}

private void recordAction(ClipLauncherSlot emptySlot) {
private void recordAction(final ClipLauncherSlot emptySlot) {
emptySlot.launch();
transport.isClipLauncherOverdubEnabled().set(true);
}

public void duplicateContent() {
mainCursorClip.duplicateContent();
}

public void quantize(final double amount) {
mainCursorClip.quantize(amount);
}

public void clearSteps() {
mainCursorClip.clearSteps();
}

public void transpose(final int semitones) {
mainCursorClip.transpose(semitones);
}

public void focusOnNextEmpty(Consumer<ClipLauncherSlot> postCreation) {
public void focusOnNextEmpty(final Consumer<ClipLauncherSlot> postCreation) {
if (focusSlotIsEmpty()) {
postCreation.accept(focusSlot);
} else {
getFirstEmptySlot(selectedSlotIndex) //
.ifPresentOrElse(slot -> postCreation.accept(slot), //
() -> ensureEmptySlot(postCreation));
.ifPresentOrElse(
slot -> postCreation.accept(slot), //
() -> ensureEmptySlot(postCreation));
}
}

private void ensureEmptySlot(Consumer<ClipLauncherSlot> postCreation) {
private void ensureEmptySlot(final Consumer<ClipLauncherSlot> postCreation) {
project.createScene();
host.scheduleTask(() -> getFirstEmptySlot(selectedSlotIndex).ifPresent(newSlot -> postCreation.accept(newSlot)),
50);
host.scheduleTask(
() -> getFirstEmptySlot(selectedSlotIndex).ifPresent(newSlot -> postCreation.accept(newSlot)),
50);
}

private boolean focusSlotIsEmpty() {
return focusSlot != null && !focusSlot.hasContent().get() && focusSlot.exists().get();
}

private Optional<ClipLauncherSlot> getFirstEmptySlot(int startIndex) {
int start = startIndex < 0 ? 0 : startIndex;
private Optional<ClipLauncherSlot> getFirstEmptySlot(final int startIndex) {
final int start = startIndex < 0 ? 0 : startIndex;
for (int i = start; i < slotBank.getSizeOfBank(); i++) {
final ClipLauncherSlot slot = slotBank.getItemAt(i);
if (!slot.hasContent().get() && slot.exists().get()) {
Expand All @@ -157,8 +166,8 @@ private Optional<ClipLauncherSlot> getFirstEmptySlot(int startIndex) {
}
return Optional.empty();
}

public void clearNotes(int noteToClear) {
public void clearNotes(final int noteToClear) {
mainCursorClip.clearStepsAtY(0, noteToClear);
}
}
Loading
Loading