Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Horizontal Shift for Audio Clips #141

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CommunityFeatures.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ Synchronization modes accessible through the "LFO SYNC" shortcut.

### Kit Keyboard View
- ([#112]) All-new use for the "keyboard" button in kit clips, uses the main pad grid for MPC-style 16 level playing. Horizonatal encoder scrolls by one pad at a time, allowing positioning drums left to right, and vertical encoder jumps vertically by rows.

### Audio Clip View
- ([#141]) Holding the vertical encoder down while turning the horizontal encoder will shift the clip along the underlying audio file, similar to the same interface for instrument clips.
topisani marked this conversation as resolved.
Show resolved Hide resolved


<h1 id="runtime-features">Runtime settings aka Community Features Menu</h1>
Expand Down Expand Up @@ -74,3 +77,4 @@ This list includes all preprocessor switches that can alter firmware behaviour a
[#103]: https://github.com/SynthstromAudible/DelugeFirmware/pull/103
[#112]: https://github.com/SynthstromAudible/DelugeFirmware/pull/112
[#122]: https://github.com/SynthstromAudible/DelugeFirmware/pull/122
[#141]: https://github.com/SynthstromAudible/DelugeFirmware/pull/141
101 changes: 81 additions & 20 deletions src/deluge/gui/views/clip_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,23 @@
#include "model/clip/clip_minder.h"
#include "gui/views/view.h"
#include "definitions.h"
#include "model/consequence/consequence_clip_horizontal_shift.h"
#include "memory/general_memory_allocator.h"
#include <new>

static Clip* getCurrentClip() {
return currentSong->currentClip;
}
topisani marked this conversation as resolved.
Show resolved Hide resolved

ClipView::ClipView() {
}

unsigned int ClipView::getMaxZoom() {
return currentSong->currentClip->getMaxZoom();
return getCurrentClip()->getMaxZoom();
}

uint32_t ClipView::getMaxLength() {
return currentSong->currentClip->getMaxLength();
return getCurrentClip()->getMaxLength();
}

void ClipView::focusRegained() {
Expand All @@ -48,14 +55,14 @@ void ClipView::focusRegained() {
int ClipView::buttonAction(int x, int y, bool on, bool inCardRoutine) {

// Horizontal encoder button press-down - don't let it do its zoom level thing if zooming etc not currently accessible
if (x == xEncButtonX && y == xEncButtonY && on && !currentSong->currentClip->currentlyScrollableAndZoomable()) {}
if (x == xEncButtonX && y == xEncButtonY && on && !getCurrentClip()->currentlyScrollableAndZoomable()) {}

#ifdef BUTTON_SEQUENCE_DIRECTION_X
else if (x == BUTTON_SEQUENCE_DIRECTION_X && y == BUTTON_SEQUENCE_DIRECTION_Y) {
if (on && isNoUIModeActive()) {
currentSong->currentClip->sequenceDirection++;
if (currentSong->currentClip->sequenceDirection == NUM_SEQUENCE_DIRECTION_OPTIONS) {
currentSong->currentClip->sequenceDirection = 0;
getCurrentClip()->sequenceDirection++;
if (getCurrentClip()->sequenceDirection == NUM_SEQUENCE_DIRECTION_OPTIONS) {
getCurrentClip()->sequenceDirection = 0;
}
view.setModLedStates();
}
Expand All @@ -78,7 +85,7 @@ Action* ClipView::lengthenClip(int32_t newLength) {
// If the last action was a shorten, undo it
bool undoing = (actionLogger.firstAction[BEFORE] && actionLogger.firstAction[BEFORE]->openForAdditions
&& actionLogger.firstAction[BEFORE]->type == ACTION_CLIP_LENGTH_DECREASE
&& actionLogger.firstAction[BEFORE]->currentClip == currentSong->currentClip);
&& actionLogger.firstAction[BEFORE]->currentClip == getCurrentClip());

if (undoing) {
allowResyncingDuringClipLengthChange =
Expand All @@ -88,16 +95,16 @@ Action* ClipView::lengthenClip(int32_t newLength) {
}

// Only if that didn't get us directly to the correct length, manually set length. This will do a resync if playback active
if (currentSong->currentClip->loopLength != newLength) {
int actionType = (newLength < currentSong->currentClip->loopLength) ? ACTION_CLIP_LENGTH_DECREASE
: ACTION_CLIP_LENGTH_INCREASE;
if (getCurrentClip()->loopLength != newLength) {
int actionType =
(newLength < getCurrentClip()->loopLength) ? ACTION_CLIP_LENGTH_DECREASE : ACTION_CLIP_LENGTH_INCREASE;

action = actionLogger.getNewAction(actionType, true);
if (action && action->currentClip != currentSong->currentClip) {
if (action && action->currentClip != getCurrentClip()) {
action = actionLogger.getNewAction(actionType, false);
}

currentSong->setClipLength(currentSong->currentClip, newLength, action);
currentSong->setClipLength(getCurrentClip(), newLength, action);
}

// Otherwise, do the resync that we missed out on doing
Expand All @@ -119,12 +126,12 @@ Action* ClipView::shortenClip(int32_t newLength) {
Action* action = NULL;

action = actionLogger.getNewAction(ACTION_CLIP_LENGTH_DECREASE, true);
if (action && action->currentClip != currentSong->currentClip) {
if (action && action->currentClip != getCurrentClip()) {
action = actionLogger.getNewAction(ACTION_CLIP_LENGTH_DECREASE, false);
}

currentSong->setClipLength(
currentSong->currentClip, newLength,
getCurrentClip(), newLength,
action); // Subsequently shortening by more squares won't cause additional Consequences to be added to the same
// Action - it checks, and only stores the data (snapshots and original length) once
return action;
Expand All @@ -137,12 +144,12 @@ int ClipView::horizontalEncoderAction(int offset) {
&& (Buttons::isShiftButtonPressed() || Buttons::isButtonPressed(clipViewButtonX, clipViewButtonY))) {

// If tempoless recording, don't allow
if (!currentSong->currentClip->currentlyScrollableAndZoomable()) {
if (!getCurrentClip()->currentlyScrollableAndZoomable()) {
numericDriver.displayPopup(HAVE_OLED ? "Can't edit length" : "CANT");
return ACTION_RESULT_DEALT_WITH;
}

uint32_t oldLength = currentSong->currentClip->loopLength;
uint32_t oldLength = getCurrentClip()->loopLength;

// If we're not scrolled all the way to the right, go there now
if (scrollRightToEndOfLengthIfNecessary(oldLength)) {
Expand Down Expand Up @@ -214,11 +221,65 @@ int ClipView::horizontalEncoderAction(int offset) {
return ACTION_RESULT_DEALT_WITH;
}

// Or, maybe shift everything horizontally
else if ((isNoUIModeActive() && Buttons::isButtonPressed(yEncButtonX, yEncButtonY))
|| (isUIModeActiveExclusively(UI_MODE_HOLDING_HORIZONTAL_ENCODER_BUTTON)
&& Buttons::isButtonPressed(clipViewButtonX, clipViewButtonY))) {
if (sdRoutineLock) return ACTION_RESULT_REMIND_ME_OUTSIDE_CARD_ROUTINE; // Just be safe - maybe not necessary
int squareSize = getPosFromSquare(1) - getPosFromSquare(0);
int shiftAmount = offset * squareSize;
Clip* clip = getCurrentClip();

char modelStackMemory[MODEL_STACK_MAX_SIZE];
ModelStackWithTimelineCounter* modelStack = currentSong->setupModelStackWithCurrentClip(modelStackMemory);

bool wasShifted = clip->shiftHorizontally(modelStack, shiftAmount);
if (!wasShifted) {
// No need to show the user why it didnt succeed, usually these cases are fairly trivial
return ACTION_RESULT_DEALT_WITH;
}

uiNeedsRendering(this, 0xFFFFFFFF, 0);

// If possible, just modify a previous Action to add this new shift amount to it.
Action* action = actionLogger.firstAction[BEFORE];
if (action && action->type == ACTION_CLIP_HORIZONTAL_SHIFT && action->openForAdditions
&& action->currentClip == clip) {

// If there's no Consequence in the Action, that's probably because we deleted it a previous time with the code just below.
// Or possibly because the Action was created but there wasn't enough RAM to create the Consequence. Anyway, just go add a consequence now.
if (!action->firstConsequence) goto addConsequenceToAction;

ConsequenceClipHorizontalShift* consequence = (ConsequenceClipHorizontalShift*)action->firstConsequence;
consequence->amount += shiftAmount;

// It might look tempting that if we've completed one whole loop, we could delete the Consequence because everything would be back the same -
// but no! Remember different NoteRows might have different lengths.
}

// Or if no previous Action, go create a new one now.
else {

action = actionLogger.getNewAction(ACTION_CLIP_HORIZONTAL_SHIFT, ACTION_ADDITION_NOT_ALLOWED);
if (action) {
addConsequenceToAction:
void* consMemory = generalMemoryAllocator.alloc(sizeof(ConsequenceClipHorizontalShift));

if (consMemory) {
ConsequenceClipHorizontalShift* newConsequence =
new (consMemory) ConsequenceClipHorizontalShift(shiftAmount);
action->addConsequence(newConsequence);
}
}
}
return ACTION_RESULT_DEALT_WITH;
}

// Or, if shift button not pressed...
else {

// If tempoless recording, don't allow
if (!currentSong->currentClip->currentlyScrollableAndZoomable()) {
if (!getCurrentClip()->currentlyScrollableAndZoomable()) {
return ACTION_RESULT_DEALT_WITH;
}

Expand Down Expand Up @@ -268,14 +329,14 @@ int32_t ClipView::getLengthExtendAmount(int32_t square) {

int ClipView::getTickSquare() {

int newTickSquare = getSquareFromPos(currentSong->currentClip->getLivePos());
int newTickSquare = getSquareFromPos(getCurrentClip()->getLivePos());

// See if we maybe want to do an auto-scroll
if (currentSong->currentClip->getCurrentlyRecordingLinearly()) {
if (getCurrentClip()->getCurrentlyRecordingLinearly()) {

if (newTickSquare == displayWidth && (!currentUIMode || currentUIMode == UI_MODE_AUDITIONING)
&& getCurrentUI() == this && // currentPlaybackMode == &session &&
(!currentSong->currentClip->armState || xScrollBeforeFollowingAutoExtendingLinearRecording != -1)) {
(!getCurrentClip()->armState || xScrollBeforeFollowingAutoExtendingLinearRecording != -1)) {

if (xScrollBeforeFollowingAutoExtendingLinearRecording == -1) {
xScrollBeforeFollowingAutoExtendingLinearRecording = currentSong->xScroll[NAVIGATION_CLIP];
Expand Down
55 changes: 0 additions & 55 deletions src/deluge/gui/views/instrument_clip_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
#include "gui/ui/rename/rename_drum_ui.h"
#include "model/song/song.h"
#include "model/clip/clip.h"
#include "model/consequence/consequence_instrument_clip_horizontal_shift.h"
#include "model/consequence/consequence_note_array_change.h"
#include "gui/menu_item/colour.h"
#include "hid/led/pad_leds.h"
Expand Down Expand Up @@ -4184,60 +4183,6 @@ int InstrumentClipView::horizontalEncoderAction(int offset) {
return ACTION_RESULT_DEALT_WITH;
}

// Or, maybe shift everything horizontally
else if ((isNoUIModeActive() && Buttons::isButtonPressed(yEncButtonX, yEncButtonY))
|| (isUIModeActiveExclusively(UI_MODE_HOLDING_HORIZONTAL_ENCODER_BUTTON)
&& Buttons::isButtonPressed(clipViewButtonX, clipViewButtonY))) {
if (sdRoutineLock) {
return ACTION_RESULT_REMIND_ME_OUTSIDE_CARD_ROUTINE; // Just be safe - maybe not necessary
}
int squareSize = getPosFromSquare(1) - getPosFromSquare(0);
int shiftAmount = offset * squareSize;
InstrumentClip* clip = getCurrentClip();

char modelStackMemory[MODEL_STACK_MAX_SIZE];
ModelStackWithTimelineCounter* modelStack = currentSong->setupModelStackWithCurrentClip(modelStackMemory);

clip->shiftHorizontally(modelStack, shiftAmount);
uiNeedsRendering(this, 0xFFFFFFFF, 0);

// If possible, just modify a previous Action to add this new shift amount to it.
Action* action = actionLogger.firstAction[BEFORE];
if (action && action->type == ACTION_INSTRUMENT_CLIP_HORIZONTAL_SHIFT && action->openForAdditions
&& action->currentClip == clip) {

// If there's no Consequence in the Action, that's probably because we deleted it a previous time with the code just below.
// Or possibly because the Action was created but there wasn't enough RAM to create the Consequence. Anyway, just go add a consequence now.
if (!action->firstConsequence) {
goto addConsequenceToAction;
}

ConsequenceInstrumentClipHorizontalShift* consequence =
(ConsequenceInstrumentClipHorizontalShift*)action->firstConsequence;
consequence->amount += shiftAmount;

// It might look tempting that if we've completed one whole loop, we could delete the Consequence because everything would be back the same -
// but no! Remember different NoteRows might have different lengths.
}

// Or if no previous Action, go create a new one now.
else {

action = actionLogger.getNewAction(ACTION_INSTRUMENT_CLIP_HORIZONTAL_SHIFT, ACTION_ADDITION_NOT_ALLOWED);
if (action) {
addConsequenceToAction:
void* consMemory = generalMemoryAllocator.alloc(sizeof(ConsequenceInstrumentClipHorizontalShift));

if (consMemory) {
ConsequenceInstrumentClipHorizontalShift* newConsequence =
new (consMemory) ConsequenceInstrumentClipHorizontalShift(shiftAmount);
action->addConsequence(newConsequence);
}
}
}
return ACTION_RESULT_DEALT_WITH;
}

// Or, let parent deal with it
else {
return ClipView::horizontalEncoderAction(offset);
Expand Down
2 changes: 1 addition & 1 deletion src/deluge/model/action/action.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class ModelStack;
#define ACTION_ARRANGEMENT_TIME_CONTRACT 17
#define ACTION_ARRANGEMENT_CLEAR 18
#define ACTION_ARRANGEMENT_RECORD 19
#define ACTION_INSTRUMENT_CLIP_HORIZONTAL_SHIFT 20
#define ACTION_CLIP_HORIZONTAL_SHIFT 20
#define ACTION_NOTE_NUDGE 21
#define ACTION_NOTE_REPEAT_EDIT 22
#define ACTION_EUCLIDEAN_NUM_EVENTS_EDIT 23
Expand Down
39 changes: 39 additions & 0 deletions src/deluge/model/clip/audio_clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,45 @@ void AudioClip::setPos(ModelStackWithTimelineCounter* modelStack, int32_t newPos
setPosForParamManagers(modelStack, useActualPosForParamManagers);
}

bool AudioClip::shiftHorizontally(ModelStackWithTimelineCounter* modelStack, int amount) {
// No horizontal shift when recording
if (recorder) return false;
// No horizontal shift when no sample is loaded
if (!sampleHolder.audioFile) return false;

int64_t newStartPos = int64_t(sampleHolder.startPos) - getSamplesFromTicks(amount);
uint64_t sampleLength = ((Sample*)sampleHolder.audioFile)->lengthInSamples;

if (newStartPos < 0 || newStartPos > sampleLength) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

startPos == sampleLength is OK?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same thought, but the sample marker editor allows it, so i assume it is ok

if (newMarkerPos > waveformBasicNavigator.sample->lengthInSamples)
newMarkerPos = waveformBasicNavigator.sample->lengthInSamples;

return false;
}

if (paramManager.containsAnyParamCollectionsIncludingExpression()) {
paramManager.shiftHorizontally(
modelStack->addOtherTwoThingsButNoNoteRow(output->toModControllable(), &paramManager), amount, loopLength);
}

uint64_t length = sampleHolder.endPos - sampleHolder.startPos;

// Stop the clip if it is playing
bool active = (playbackHandler.isEitherClockActive() && modelStack->song->isClipActive(this) && voiceSample);
unassignVoiceSample();

sampleHolder.startPos = newStartPos;
sampleHolder.endPos = newStartPos + length;

sampleHolder.claimClusterReasons(sampleControls.reversed, CLUSTER_LOAD_IMMEDIATELY_OR_ENQUEUE);

if (active) {
expectEvent();
reGetParameterAutomation(modelStack);

// Resume the clip if it was playing before
currentSong->currentClip->resumePlayback(modelStack, true);
}
return true;
}

uint64_t AudioClip::getCullImmunity() {
uint32_t distanceFromEnd = loopLength - getLivePos();
// We're gonna cull time-stretching ones first
Expand Down
2 changes: 2 additions & 0 deletions src/deluge/model/clip/audio_clip.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class AudioClip final : public Clip {
void sampleZoneChanged(ModelStackWithTimelineCounter const* modelStack);
int64_t getNumSamplesTilLoop(ModelStackWithTimelineCounter* modelStack);
void setPos(ModelStackWithTimelineCounter* modelStack, int32_t newPos, bool useActualPosForParamManagers);
/// Return true if successfully shifted, as clip cannot be shifted past beginning
bool shiftHorizontally(ModelStackWithTimelineCounter* modelStack, int amount);

int readFromFile(Song* song);
void writeDataToFile(Song* song);
Expand Down
2 changes: 2 additions & 0 deletions src/deluge/model/clip/clip.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ class Clip : public TimelineCounter {
void setSequenceDirectionMode(ModelStackWithTimelineCounter* modelStack, int newSequenceDirection);
bool possiblyCloneForArrangementRecording(ModelStackWithTimelineCounter* modelStack);
virtual void incrementPos(ModelStackWithTimelineCounter* modelStack, int32_t numTicks);
/// Return true if successfully shifted
virtual bool shiftHorizontally(ModelStackWithTimelineCounter* modelStack, int amount) = 0;

// ----- PlayPositionCounter implementation -------
int32_t getLoopLength();
Expand Down
3 changes: 2 additions & 1 deletion src/deluge/model/clip/instrument_clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3181,7 +3181,7 @@ NoteRow* InstrumentClip::getNoteRowFromId(int id) {
}
}

void InstrumentClip::shiftHorizontally(ModelStackWithTimelineCounter* modelStack, int amount) {
bool InstrumentClip::shiftHorizontally(ModelStackWithTimelineCounter* modelStack, int amount) {

if (paramManager.containsAnyParamCollectionsIncludingExpression()) {
paramManager.shiftHorizontally(
Expand All @@ -3199,6 +3199,7 @@ void InstrumentClip::shiftHorizontally(ModelStackWithTimelineCounter* modelStack
expectEvent();
reGetParameterAutomation(modelStack); // Re-gets all NoteRow-level param automation too
}
return true;
}

void InstrumentClip::shiftOnlyOneNoteRowHorizontally(ModelStackWithNoteRow* modelStack, int shiftAmount) {
Expand Down
3 changes: 2 additions & 1 deletion src/deluge/model/clip/instrument_clip.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ class InstrumentClip final : public Clip {
void restoreBackedUpParamManagerMIDI(ModelStackWithModControllable* modelStack);
int getNoteRowId(NoteRow* noteRow, int noteRowIndex);
NoteRow* getNoteRowFromId(int id);
void shiftHorizontally(ModelStackWithTimelineCounter* modelStack, int amount);
/// Return true if successfully shifted. Instrument clips always succeed
bool shiftHorizontally(ModelStackWithTimelineCounter* modelStack, int amount);
bool containsAnyNotes();
ModelStackWithNoteRow* getNoteRowOnScreen(int yDisplay, ModelStackWithTimelineCounter* modelStack);
NoteRow* getNoteRowOnScreen(int yDisplay, Song* song, int* getIndex = NULL);
Expand Down