Skip to content

Commit

Permalink
swing interval shortcut
Browse files Browse the repository at this point in the history
- Hold TAP TEMPO and turn TEMPO to adjust the current swing interval.

- Hold TAP TEMPO and press TEMPO to show the current swing interval.

- On OLED, both swing interval and swing amount are always displayed
  together, with a two-row popup. On 7seg only the main value for
  each action is shown.

- Swing Interval menu moved from SETTINGS to SONG.

- Explicit NUM_SWING_INTERVALS value instead of SYNC_TYPE_TRIPLET,
  which is a bit too magical.

- new utility function mod(): like %, but always non-negative

- Extract sync level and type logic from SyncLevel menu item to
  more reusable location, add tests.

- For both swing amount and interval when using the tempo encoder,
  on first encoder click display current value instead of changing it

- Splits out some of the new test mocks into more reusable pieces.

- DEFAULTS > SWING AMOUNT, SWING INTERVAL instead of just
  amount. Default menus for Swing amount and interval both say "Default
  swing" at the top, because longer text would not fit, and the menu label
  does not scroll. On 7-seg SWIN becomes SWIA, and swing interval is
  denoted by SWII.
  • Loading branch information
nikodemus committed Jul 14, 2024
1 parent aeafd42 commit 39fdcda
Show file tree
Hide file tree
Showing 47 changed files with 408 additions and 172 deletions.
7 changes: 6 additions & 1 deletion docs/community_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,18 @@ Here is a list of general improvements that have been made, ordered from newest
updating the kit row selection, you can now control the parameters for that kit row. With midi follow and midi
feedback enabled, this will also send updated cc feedback for the new kit row selection.

#### 3.4 - Tempo
#### 3.4 - Tempo & Swing

- ([#178]) New option (`FINE TEMPO` in the `COMMUNITY FEATURES` menu). Inverts the push+turn behavior of the `TEMPO`
encoder. With this option enabled the tempo changes by 1 when unpushed and ~4 when pushed (vs ~4 unpushed and 1 pushed
in the official firmware).
- This feature is `ON` by default and can be set to `ON` or `OFF` via `SETTINGS > COMMUNITY FEATURES`.

- ([#2264]) Swing interval can now be changed by holding `TAP TEMPO` button while turning the `TEMPO` encoder. To view current
swing interval hold `TAP TEMPO` and press `TEMPO`. The menu for setting the swing interval has been moved from
`SETTINGS > SWING INTERVAL` to `SONG > SWING INTERVAL`. Additionally, when changing either swing amount or interval using the
`TEMPO` encoder, first encoder detent shows the current swing without changing it, with subsequent ones editing it.

#### 3.5 - Kits

- ([#395]) Load synth presets into kit rows by holding the row's `AUDITION` + `SYNTH`. Saving can be done by holding the
Expand Down
6 changes: 4 additions & 2 deletions docs/menu_hierarchies.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ The Settings menu contains the following menu hierarchy:
- Disabled (OFF)
- Enabled (ON)
- Tempo (TEMP)
- Swing (SWIN)
- Swing Amount (SWIA)
- Swing Interval (SWII)
- Key
- Scale (SCAL)
- Major (MAJO)
Expand Down Expand Up @@ -222,7 +223,7 @@ The Settings menu contains the following menu hierarchy:
- Hold Press Time (HOLD)
</details>

<details><summary>Swing Interval (SWIN)</summary>
<details><summary>Swing Interval (SWII)</summary>
NOTE: These options can change depending on how your default resolution is set

- 2-Bar
Expand Down Expand Up @@ -503,6 +504,7 @@ The Song menu contains the following menu hierarchy:
- Decimation (DECI)
- Bitcrush (CRUS)
</details>
- SWING INTERVAL (SWII)
<details><summary>MIDI Loopback (M LP)</summary>

- Disabled (OFF)
Expand Down
22 changes: 0 additions & 22 deletions src/definitions_cxx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -403,28 +403,6 @@ enum class LFOType : uint8_t {

constexpr int32_t kNumLFOTypes = util::to_underlying(LFOType::RANDOM_WALK) + 1;

// SyncType values correspond to the index of the first option of the specific
// type in the selection menu. There are 9 different levels for each type (see
// also SyncLevel)
enum SyncType : uint8_t {
SYNC_TYPE_EVEN = 0,
SYNC_TYPE_TRIPLET = 10,
SYNC_TYPE_DOTTED = 19,
};

enum SyncLevel : uint8_t {
SYNC_LEVEL_NONE = 0,
SYNC_LEVEL_WHOLE = 1,
SYNC_LEVEL_2ND = 2,
SYNC_LEVEL_4TH = 3,
SYNC_LEVEL_8TH = 4,
SYNC_LEVEL_16TH = 5,
SYNC_LEVEL_32ND = 6,
SYNC_LEVEL_64TH = 7,
SYNC_LEVEL_128TH = 8,
SYNC_LEVEL_256TH = 9,
};

enum class SynthMode : uint8_t {
SUBTRACTIVE,
FM,
Expand Down
1 change: 1 addition & 0 deletions src/deluge/dsp/delay/delay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "dsp/stereo_sample.h"
#include "io/debug/log.h"
#include "memory/general_memory_allocator.h"
#include "model/sync.h"
#include "processing/engines/audio_engine.h"
#include <cstdlib>
#include <ranges>
Expand Down
2 changes: 2 additions & 0 deletions src/deluge/dsp/delay/delay.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "definitions_cxx.hpp"
#include "dsp/convolution/impulse_response_processor.h"
#include "dsp/delay/delay_buffer.h"
#include "model/sync.h"

#include <cstdint>
#include <span>

Expand Down
4 changes: 2 additions & 2 deletions src/deluge/gui/l10n/english.json
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,6 @@
"STRING_FOR_ROOM_SIZE": "Room size",
"STRING_FOR_SUB_BANK": "Sub-bank",
"STRING_FOR_PLAY_DIRECTION": "Play direction",
"STRING_FOR_SWING_INTERVAL": "Swing interval",
"STRING_FOR_SHORTCUTS_VERSION": "Shortcuts version",
"STRING_FOR_KEYBOARD_FOR_TEXT": "Keyboard for text",
"STRING_FOR_LOOP_MARGINS": "Loop margins",
Expand Down Expand Up @@ -608,7 +607,8 @@
"STRING_FOR_OUTPUT_PPQN": "Output PPQN",
"STRING_FOR_TEMPO": "TEMPO",
"STRING_FOR_DEFAULT_TEMPO": "Default tempo",
"STRING_FOR_SWING": "SWING",
"STRING_FOR_SWING_AMOUNT": "Swing amount",
"STRING_FOR_SWING_INTERVAL": "Swing interval",
"STRING_FOR_DEFAULT_SWING": "Default swing",
"STRING_FOR_KEY": "KEY",
"STRING_FOR_DEFAULT_KEY": "Default key",
Expand Down
4 changes: 2 additions & 2 deletions src/deluge/gui/l10n/g_english.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,6 @@ PLACE_SDRAM_DATA Language english{
{STRING_FOR_ROOM_SIZE, "Room size"},
{STRING_FOR_SUB_BANK, "Sub-bank"},
{STRING_FOR_PLAY_DIRECTION, "Play direction"},
{STRING_FOR_SWING_INTERVAL, "Swing interval"},
{STRING_FOR_SHORTCUTS_VERSION, "Shortcuts version"},
{STRING_FOR_KEYBOARD_FOR_TEXT, "Keyboard for text"},
{STRING_FOR_LOOP_MARGINS, "Loop margins"},
Expand Down Expand Up @@ -562,7 +561,8 @@ PLACE_SDRAM_DATA Language english{
{STRING_FOR_OUTPUT_PPQN, "Output PPQN"},
{STRING_FOR_TEMPO, "TEMPO"},
{STRING_FOR_DEFAULT_TEMPO, "Default tempo"},
{STRING_FOR_SWING, "SWING"},
{STRING_FOR_SWING_AMOUNT, "Swing amount"},
{STRING_FOR_SWING_INTERVAL, "Swing interval"},
{STRING_FOR_DEFAULT_SWING, "Default swing"},
{STRING_FOR_KEY, "KEY"},
{STRING_FOR_DEFAULT_KEY, "Default key"},
Expand Down
3 changes: 2 additions & 1 deletion src/deluge/gui/l10n/g_seven_segment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ PLACE_SDRAM_DATA Language seven_segment{
{STRING_FOR_BITCRUSH, "CRUSH"},
{STRING_FOR_SUB_BANK, "SUB"},
{STRING_FOR_PLAY_DIRECTION, "DIRECTION"},
{STRING_FOR_SWING_INTERVAL, "SWIN"},
{STRING_FOR_SWING_AMOUNT, "SWIA"},
{STRING_FOR_SWING_INTERVAL, "SWII"},
{STRING_FOR_SHORTCUTS_VERSION, "SHOR"},
{STRING_FOR_KEYBOARD_FOR_TEXT, "KEYB"},
{STRING_FOR_LOOP_MARGINS, "MARGINS"},
Expand Down
3 changes: 2 additions & 1 deletion src/deluge/gui/l10n/seven_segment.json
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,8 @@
"STRING_FOR_BITCRUSH": "CRUSH",
"STRING_FOR_SUB_BANK": "SUB",
"STRING_FOR_PLAY_DIRECTION": "DIRECTION",
"STRING_FOR_SWING_INTERVAL": "SWIN",
"STRING_FOR_SWING_AMOUNT": "SWIA",
"STRING_FOR_SWING_INTERVAL": "SWII",
"STRING_FOR_SHORTCUTS_VERSION": "SHOR",
"STRING_FOR_KEYBOARD_FOR_TEXT": "KEYB",
"STRING_FOR_LOOP_MARGINS": "MARGINS",
Expand Down
4 changes: 2 additions & 2 deletions src/deluge/gui/l10n/strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,6 @@ enum class String : size_t {
STRING_FOR_BITCRUSH,
STRING_FOR_SUB_BANK,
STRING_FOR_PLAY_DIRECTION,
STRING_FOR_SWING_INTERVAL,
STRING_FOR_SHORTCUTS_VERSION,
STRING_FOR_KEYBOARD_FOR_TEXT,
STRING_FOR_LOOP_MARGINS,
Expand Down Expand Up @@ -608,7 +607,8 @@ enum class String : size_t {
STRING_FOR_OUTPUT_PPQN,
STRING_FOR_TEMPO,
STRING_FOR_DEFAULT_TEMPO,
STRING_FOR_SWING,
STRING_FOR_SWING_AMOUNT,
STRING_FOR_SWING_INTERVAL,
STRING_FOR_DEFAULT_SWING,
STRING_FOR_KEY,
STRING_FOR_DEFAULT_KEY,
Expand Down
4 changes: 2 additions & 2 deletions src/deluge/gui/menu_item/arpeggiator/sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ class Sync final : public SyncLevel {
soundEditor.currentArpSettings->syncLevel));
}
void writeCurrentValue() {
soundEditor.currentArpSettings->syncType = menuOptionToSyncType(this->getValue());
soundEditor.currentArpSettings->syncLevel = menuOptionToSyncLevel(this->getValue());
soundEditor.currentArpSettings->syncType = syncValueToSyncType(this->getValue());
soundEditor.currentArpSettings->syncLevel = syncValueToSyncLevel(this->getValue());
}
};

Expand Down
37 changes: 37 additions & 0 deletions src/deluge/gui/menu_item/defaults/swing_interval.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2014-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/menu_item/swing/interval.h"
#include "storage/flash_storage.h"

namespace deluge::gui::menu_item::defaults {

using namespace deluge::gui::menu_item::swing;

class SwingInterval final : public Interval {
public:
using Interval::Interval;
void readCurrentValue() override { this->setValue(FlashStorage::defaultSwingInterval); }
void writeCurrentValue() override {
FlashStorage::defaultSwingInterval = this->getValue();
// NOTE: different defautls are a bit inconsistent in how they treat current values.
// eg. Velocity immediately changes the default velocity of the current song, but tempo
// and swing don't. So We don't either.
}
};

} // namespace deluge::gui::menu_item::defaults
4 changes: 2 additions & 2 deletions src/deluge/gui/menu_item/delay/sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class Sync final : public SyncLevel {
soundEditor.currentModControllable->delay.syncLevel));
}
void writeCurrentValue() override {
soundEditor.currentModControllable->delay.syncType = menuOptionToSyncType(this->getValue());
soundEditor.currentModControllable->delay.syncLevel = menuOptionToSyncLevel(this->getValue());
soundEditor.currentModControllable->delay.syncType = syncValueToSyncType(this->getValue());
soundEditor.currentModControllable->delay.syncLevel = syncValueToSyncLevel(this->getValue());
}
};
} // namespace deluge::gui::menu_item::delay
4 changes: 2 additions & 2 deletions src/deluge/gui/menu_item/lfo/sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class Sync final : public SyncLevel {
soundEditor.currentSound->lfoConfig[lfoId_].syncLevel));
}
void writeCurrentValue() {
soundEditor.currentSound->lfoConfig[lfoId_].syncType = menuOptionToSyncType(this->getValue());
soundEditor.currentSound->lfoConfig[lfoId_].syncLevel = menuOptionToSyncLevel(this->getValue());
soundEditor.currentSound->lfoConfig[lfoId_].syncType = syncValueToSyncType(this->getValue());
soundEditor.currentSound->lfoConfig[lfoId_].syncLevel = syncValueToSyncLevel(this->getValue());
// This fires unnecessarily for LFO2 assignments as well, but that's ok. It's not
// entirely clear if we really need this for the LFO1, even: maybe the clock-driven resyncs
// would be enough?
Expand Down
4 changes: 2 additions & 2 deletions src/deluge/gui/menu_item/sidechain/sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class Sync final : public SyncLevel {
soundEditor.currentSidechain->syncLevel));
}
void writeCurrentValue() override {
soundEditor.currentSidechain->syncType = menuOptionToSyncType(this->getValue());
soundEditor.currentSidechain->syncLevel = menuOptionToSyncLevel(this->getValue());
soundEditor.currentSidechain->syncType = syncValueToSyncType(this->getValue());
soundEditor.currentSidechain->syncLevel = syncValueToSyncLevel(this->getValue());
AudioEngine::mustUpdateReverbParamsBeforeNextRender = true;
}
bool isRelevant(ModControllableAudio* modControllable, int32_t whichThing) override {
Expand Down
18 changes: 4 additions & 14 deletions src/deluge/gui/menu_item/swing/interval.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,16 @@

namespace deluge::gui::menu_item::swing {

class Interval final : public SyncLevel {
class Interval : public SyncLevel {
public:
using SyncLevel::SyncLevel;

void readCurrentValue() override { this->setValue(currentSong->swingInterval); }
void writeCurrentValue() override { currentSong->changeSwingInterval(this->getValue()); }
// triplet/dotted not yet supported
size_t size() override { return SYNC_TYPE_TRIPLET; }
void selectEncoderAction(int32_t offset) override { // So that there's no "off" option
this->setValue(this->getValue() + offset);
int32_t numOptions = this->size();

// Wrap value
if (this->getValue() >= numOptions) {
this->setValue(this->getValue() - (numOptions - 1));
}
else if (this->getValue() < 1) {
this->setValue(this->getValue() + (numOptions - 1));
}

size_t size() override { return NUM_SWING_INTERVALS; }
void selectEncoderAction(int32_t offset) override {
this->setValue(wrapSwingIntervalSyncLevel(this->getValue() + offset));
Value::selectEncoderAction(offset);
}
};
Expand Down
58 changes: 1 addition & 57 deletions src/deluge/gui/menu_item/sync_level.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,37 +35,7 @@ void SyncLevel::drawValue() {
}

void SyncLevel::getNoteLengthName(StringBuf& buffer) {
char const* typeStr = nullptr;
uint32_t value = this->getValue();
::SyncType type{menuOptionToSyncType(value)};
::SyncLevel level{menuOptionToSyncLevel(value)};

uint32_t shift = SYNC_LEVEL_256TH - level;
uint32_t noteLength = uint32_t{3} << shift;

switch (type) {
case SYNC_TYPE_EVEN:
if (value != 0) {
typeStr = "-notes";
}
break;
case SYNC_TYPE_TRIPLET:
typeStr = "-tplts";
break;
case SYNC_TYPE_DOTTED:
typeStr = "-dtted";
break;
}

currentSong->getNoteLengthName(buffer, noteLength, typeStr);
if (typeStr != nullptr) {
int32_t magnitudeLevelBars = SYNC_LEVEL_8TH - currentSong->insideWorldTickMagnitude;
if (((type == SYNC_TYPE_TRIPLET || type == SYNC_TYPE_DOTTED) && level <= magnitudeLevelBars)
|| display->have7SEG()) {
// On OLED, getNoteLengthName handles adding this for the non-bar levels. On 7seg, always append it
buffer.append(typeStr);
}
}
syncValueToString(this->getValue(), buffer);
}

void SyncLevel::drawPixelsForOled() {
Expand All @@ -79,32 +49,6 @@ void SyncLevel::drawPixelsForOled() {
kTextBigSizeY);
}

SyncType SyncLevel::menuOptionToSyncType(int32_t option) {
if (option < SYNC_TYPE_TRIPLET) {
return SYNC_TYPE_EVEN;
}
else if (option < SYNC_TYPE_DOTTED) {
return SYNC_TYPE_TRIPLET;
}
else {
return SYNC_TYPE_DOTTED;
}
}

::SyncLevel SyncLevel::menuOptionToSyncLevel(int32_t option) {
::SyncLevel level;
if (option < SYNC_TYPE_TRIPLET) {
level = static_cast<::SyncLevel>(option);
}
else if (option < SYNC_TYPE_DOTTED) {
level = static_cast<::SyncLevel>(option - SYNC_TYPE_TRIPLET + 1);
}
else {
level = static_cast<::SyncLevel>(option - SYNC_TYPE_DOTTED + 1);
}
return level;
}

int32_t SyncLevel::syncTypeAndLevelToMenuOption(::SyncType type, ::SyncLevel level) {
return static_cast<int32_t>(type) + (static_cast<int32_t>(level) - (type != SYNC_TYPE_EVEN ? 1 : 0));
}
Expand Down
5 changes: 1 addition & 4 deletions src/deluge/gui/menu_item/sync_level.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,12 @@ namespace deluge::gui::menu_item {
class SyncLevel : public Enumeration {
public:
using Enumeration::Enumeration;
SyncType menuOptionToSyncType(int32_t option);
::SyncLevel menuOptionToSyncLevel(int32_t option);
int32_t syncTypeAndLevelToMenuOption(SyncType type, ::SyncLevel level);
size_t size() override { return 28; }
size_t size() override { return NUM_SYNC_VALUES; }

protected:
void drawValue() final;
virtual void getNoteLengthName(StringBuf& buffer);

void drawPixelsForOled() override;
};

Expand Down
2 changes: 1 addition & 1 deletion src/deluge/gui/menu_item/value_scaling.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
///
/// Special cases:
/// - arpeggiator::Sync - uses syncTypeAndLevelToMenuOption() to pack two values,
/// and menuOptionToSyncType|Level() to unpack them.
/// and syncValueToSyncType|Level() to unpack them.
///
/// As stuff is extraced and turns out to be functionally identical the dupes
/// should be eliminated.
Expand Down
Loading

0 comments on commit 39fdcda

Please sign in to comment.