Skip to content

Commit

Permalink
feature: session macro sidebar
Browse files Browse the repository at this point in the history
  • Loading branch information
bfredl committed Jul 17, 2024
1 parent dc7c128 commit 664b48e
Show file tree
Hide file tree
Showing 19 changed files with 584 additions and 23 deletions.
14 changes: 14 additions & 0 deletions docs/community_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,20 @@ Here is a list of features that have been added to the firmware as a list, group
- y1 = -26.4 to -22.1
- y0 = -30.8 to -26.5

### 4.1.9 - Song part macros

Macros are a way to quickly switch playing clips without needing to go into song view.
Within grid view, a fourth (purple) mode is used to edit macros. There are 8 macro slots
shown in the left sidebar (the section colors are hidden in this mode). To assign a macro,
first select a macro slot and then press a clip in the grid. Pressing the same clip multiple
time cycles though different modes:

- clip macro: Turn individual clip on/off
- output macro: cycle all clips for this particular clip
- section macro: activate all clips for this section

These macros than then be accessed in keyboard view of any part by selecting the "CLIP MACROS" sidebar.

### 4.2 - Clip View - General Features (Instrument and Audio Clips)

#### 4.2.1 - Filters
Expand Down
2 changes: 1 addition & 1 deletion src/definitions_cxx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ enum GridMode : uint8_t {
Unassigned1,
Unassigned2,
Unassigned3,
Unassigned4,
MAGENTA,
YELLOW,
BLUE,
GREEN,
Expand Down
86 changes: 86 additions & 0 deletions src/deluge/gui/ui/keyboard/column_controls/session.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright © 2016-2024 Synthstrom Audible Limited
*
* This file is part of The Synthstrom Audible Deluge Firmware.
*
* The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/

#include "session.h"
#include "gui/ui/keyboard/layout/column_controls.h"
#include "gui/views/session_view.h"
#include "gui/views/view.h"
#include "hid/buttons.h"
#include "playback/mode/session.h"

using namespace deluge::gui::ui::keyboard::layout;

namespace deluge::gui::ui::keyboard::controls {

void SessionColumn::renderColumn(RGB image[][kDisplayWidth + kSideBarWidth], int32_t column) {
bool armed = false;
for (int32_t y = 0; y < kDisplayHeight; ++y) {
armed |= sessionView.gridRenderMacros(column, y, false, image, nullptr);
}
if (armed) {
view.flashPlayEnable();
}
}

bool SessionColumn::handleVerticalEncoder(int8_t pad, int32_t offset) {
SessionMacro& m = currentSong->sessionMacros[pad];
int kindIndex = (int32_t)m.kind + offset;
if (kindIndex >= SessionMacroKind::NUM_KINDS) {
kindIndex = 0;
}
else if (kindIndex < 0) {
kindIndex = SessionMacroKind::NUM_KINDS - 1;
}

m.kind = (SessionMacroKind)kindIndex;

switch (m.kind) {
case CLIP_LAUNCH:
m.clip = getCurrentClip();
break;

case OUTPUT_CYCLE:
m.clip = nullptr;
m.output = getCurrentOutput();
break;

case SECTION:
m.section = getCurrentClip()->section;

default:
break;
}

return true;
};

void SessionColumn::handleLeavingColumn(ModelStackWithTimelineCounter* modelStackWithTimelineCounter,
KeyboardLayout* layout){};

void SessionColumn::handlePad(ModelStackWithTimelineCounter* modelStackWithTimelineCounter, PressedPad pad,
KeyboardLayout* layout) {

SessionMacro& m = currentSong->sessionMacros[pad.y];
if (pad.active) {}
else {
sessionView.activateMacro(pad.y, pad.padPressHeld);
}
view.flashPlayEnable();
};


} // namespace deluge::gui::ui::keyboard::controls
42 changes: 42 additions & 0 deletions src/deluge/gui/ui/keyboard/column_controls/session.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright © 2016-2024 Synthstrom Audible Limited
*
* This file is part of The Synthstrom Audible Deluge Firmware.
*
* The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "gui/ui/keyboard/column_controls/control_column.h"
#include "model/song/song.h"

namespace deluge::gui::ui::keyboard::controls {

class SessionColumn : public ControlColumn {
public:
SessionColumn() = default;

void renderColumn(RGB image[][kDisplayWidth + kSideBarWidth], int32_t column) override;
bool handleVerticalEncoder(int8_t pad, int32_t offset) override;
void handleLeavingColumn(ModelStackWithTimelineCounter* modelStackWithTimelineCounter,
KeyboardLayout* layout) override;
void handlePad(ModelStackWithTimelineCounter* modelStackWithTimelineCounter, PressedPad pad,
KeyboardLayout* layout) override;

void handleOutput(SessionMacro& m, PressedPad pad);
Clip* findNextClipForOutput(SessionMacro& m, PressedPad pad);

private:
};

} // namespace deluge::gui::ui::keyboard::controls
3 changes: 3 additions & 0 deletions src/deluge/gui/ui/keyboard/layout/column_control_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "gui/ui/keyboard/column_controls/dx.h"
#include "gui/ui/keyboard/column_controls/mod.h"
#include "gui/ui/keyboard/column_controls/scale_mode.h"
#include "gui/ui/keyboard/column_controls/session.h"
#include "gui/ui/keyboard/column_controls/song_chord_mem.h"
#include "gui/ui/keyboard/column_controls/velocity.h"

Expand All @@ -35,6 +36,7 @@ enum ColumnControlFunction : int8_t {
CHORD_MEM,
SCALE_MODE,
DX,
SESSION,
// BEAT_REPEAT,
COL_CTRL_FUNC_MAX,
};
Expand All @@ -50,6 +52,7 @@ struct ColumnControlState {
ChordMemColumn chordMemColumn{};
ScaleModeColumn scaleModeColumn{};
DXColumn dxColumn{};
SessionColumn sessionColumn{};

ColumnControlFunction leftColFunc = VELOCITY;
ColumnControlFunction rightColFunc = MOD;
Expand Down
8 changes: 8 additions & 0 deletions src/deluge/gui/ui/keyboard/layout/column_controls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const char* functionNames[][2] = {
/* CHORD_MEM */ {"CCME", "Clip Chord Memory"},
/* SCALE_MODE */ {"SMOD", "Scales"},
/* DX */ {"DX", "DX operators"},
/* SESSION */ {"SESS", "clip macros"},
/* BEAT_REPEAT */ {"BEAT", "Beat Repeat"},
};

Expand Down Expand Up @@ -204,6 +205,8 @@ ControlColumn* ColumnControlState::getColumnForFunc(ColumnControlFunction func)
return &scaleModeColumn;
case DX:
return &dxColumn;
case SESSION:
return &sessionColumn;
}
return nullptr;
}
Expand All @@ -224,6 +227,8 @@ const char* columnFunctionToString(ColumnControlFunction func) {
return "scale_mode";
case DX:
return "dx";
case SESSION:
return "session";
}
return "";
}
Expand All @@ -250,6 +255,9 @@ ColumnControlFunction stringToColumnFunction(char const* string) {
else if (!strcmp(string, "dx")) {
return DX;
}
else if (!strcmp(string, "session")) {
return SESSION;
}
else {
return VELOCITY; // unknown column, just pick the default
}
Expand Down
1 change: 1 addition & 0 deletions src/deluge/gui/ui/ui.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ extern bool pendingUIRenderingLock;
#define UI_MODE_HOLDING_STATUS_PAD 62
#define UI_MODE_IMPLODE_ANIMATION 63
#define UI_MODE_STEM_EXPORT 64
#define UI_MODE_HOLDING_SONG_BUTTON 65

#define EXCLUSIVE_UI_MODES_MASK ((uint32_t)255)

Expand Down
5 changes: 1 addition & 4 deletions src/deluge/gui/ui_timer_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ void UITimerManager::routine() {
break;

case TimerName::PLAY_ENABLE_FLASH: {
RootUI* rootUI = getRootUI();
if ((rootUI == &sessionView) || (rootUI == &performanceSessionView)) {
sessionView.flashPlayRoutine();
}
view.flashPlayRoutine();
break;
}
case TimerName::DISPLAY:
Expand Down
13 changes: 12 additions & 1 deletion src/deluge/gui/views/audio_clip_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,21 @@ ActionResult AudioClipView::buttonAction(deluge::hid::Button b, bool on, bool in

// Song view button
if (b == SESSION_VIEW) {
if (on && currentUIMode == UI_MODE_NONE) {
if (on) {
if (currentUIMode == UI_MODE_NONE) {
currentUIMode = UI_MODE_HOLDING_SONG_BUTTON;
timeSongButtonPressed = AudioEngine::audioSampleTimer;
indicator_leds::setLedState(IndicatorLED::SESSION_VIEW, true);
}

} else {
if (!isUIModeActive(UI_MODE_HOLDING_SONG_BUTTON)) {
return ActionResult::DEALT_WITH;
}
if (inCardRoutine) {
return ActionResult::REMIND_ME_OUTSIDE_CARD_ROUTINE;
}
exitUIMode(UI_MODE_HOLDING_SONG_BUTTON);

uiTimerManager.unsetTimer(TimerName::UI_SPECIFIC);

Expand Down
1 change: 1 addition & 0 deletions src/deluge/gui/views/audio_clip_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class AudioClipView final : public ClipView, public ClipMinder {
const char* getName() { return "audio_clip_view"; }

private:
uint32_t timeSongButtonPressed;
void needsRenderingDependingOnSubMode();
int32_t lastTickSquare;
bool mustRedrawTickSquares;
Expand Down
41 changes: 39 additions & 2 deletions src/deluge/gui/views/instrument_clip_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,28 @@ ActionResult InstrumentClipView::buttonAction(deluge::hid::Button b, bool on, bo

// Song view button
else if (b == SESSION_VIEW) {
if (on && currentUIMode == UI_MODE_NONE) {
if (on) {
if (currentUIMode == UI_MODE_NONE) {
currentUIMode = UI_MODE_HOLDING_SONG_BUTTON;
timeSongButtonPressed = AudioEngine::audioSampleTimer;
indicator_leds::setLedState(IndicatorLED::SESSION_VIEW, true);
uiNeedsRendering(this, 0, 0xFFFFFFFF);
}

} else {
if (!isUIModeActive(UI_MODE_HOLDING_SONG_BUTTON)) {
return ActionResult::DEALT_WITH;
}
if (inCardRoutine) {
return ActionResult::REMIND_ME_OUTSIDE_CARD_ROUTINE;
}
exitUIMode(UI_MODE_HOLDING_SONG_BUTTON);

if ((int32_t)(AudioEngine::audioSampleTimer - timeSongButtonPressed) > kShortPressTime) {
uiNeedsRendering(this, 0, 0xFFFFFFFF);
indicator_leds::setLedState(IndicatorLED::SESSION_VIEW, false);
return ActionResult::DEALT_WITH;
}

if (currentSong->lastClipInstanceEnteredStartPos != -1 || getCurrentClip()->isArrangementOnlyClip()) {
bool success = arrangerView.transitionToArrangementEditor();
Expand Down Expand Up @@ -1523,6 +1541,16 @@ ActionResult InstrumentClipView::padAction(int32_t x, int32_t y, int32_t velocit
}
view.noteRowMuteMidiLearnPadPressed(velocity, noteRow);
}
else if (isUIModeActive(UI_MODE_HOLDING_SONG_BUTTON)) {
if (sdRoutineLock) {
return ActionResult::REMIND_ME_OUTSIDE_CARD_ROUTINE;
}
if (!velocity) {
// TODO: long press..
sessionView.activateMacro(y, false);
}
return ActionResult::DEALT_WITH;
}
else if (getCurrentOutputType() == OutputType::KIT && lastAuditionedYDisplay == y
&& isUIModeActive(UI_MODE_AUDITIONING) && getNumNoteRowsAuditioning() == 1) {
if (velocity) {
Expand Down Expand Up @@ -4347,12 +4375,21 @@ bool InstrumentClipView::renderSidebar(uint32_t whichRows, RGB image[][kDisplayW
return true;
}

uint32_t macroColumn = kDisplayWidth;
bool armed = false;
for (int32_t i = 0; i < kDisplayHeight; i++) {
if (whichRows & (1 << i)) {
drawMuteSquare(getCurrentInstrumentClip()->getNoteRowOnScreen(i, currentSong), image[i], occupancyMask[i]);
if (isUIModeActive(UI_MODE_HOLDING_SONG_BUTTON)) {
armed |= sessionView.gridRenderMacros(macroColumn, i, true, image, occupancyMask);
} else {
drawMuteSquare(getCurrentInstrumentClip()->getNoteRowOnScreen(i, currentSong), image[i], occupancyMask[i]);
}
drawAuditionSquare(i, image[i]);
}
}
if (armed) {
view.flashPlayEnable();
}

return true;
}
Expand Down
1 change: 1 addition & 0 deletions src/deluge/gui/views/instrument_clip_view.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ class InstrumentClipView final : public ClipView, public InstrumentClipMinder {
uint8_t yDisplayOfNewNoteRow;

int32_t quantizeAmount;
uint32_t timeSongButtonPressed;

std::array<RGB, kDisplayHeight> rowColour;
std::array<RGB, kDisplayHeight> rowTailColour;
Expand Down
Loading

0 comments on commit 664b48e

Please sign in to comment.