Deluge Firmware 1.3.0
Build date: 2025.09.14
Loading...
Searching...
No Matches
repeat.h
1/*
2 * Copyright (c) 2014-2023 Synthstrom Audible Limited
3 *
4 * This file is part of The Synthstrom Audible Deluge Firmware.
5 *
6 * The Synthstrom Audible Deluge Firmware is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software Foundation,
8 * either version 3 of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along with this program.
15 * If not, see <https://www.gnu.org/licenses/>.
16 */
17#pragma once
18#include "definitions_cxx.hpp"
19#include "gui/menu_item/formatted_title.h"
20#include "gui/menu_item/sample/utils.h"
21#include "gui/menu_item/selection.h"
22#include "gui/ui/sound_editor.h"
23#include "gui/views/instrument_clip_view.h"
24#include "model/clip/instrument_clip.h"
25#include "model/drum/drum.h"
26#include "model/instrument/kit.h"
27#include "model/song/song.h"
28#include "processing/sound/sound_drum.h"
29
30namespace deluge::gui::menu_item::sample {
31
32class Repeat final : public Selection, public FormattedTitle {
33public:
34 Repeat(l10n::String name, l10n::String title_format_str, uint8_t source_id)
35 : Selection(name), FormattedTitle(title_format_str), source_id_{source_id} {}
36
37 [[nodiscard]] std::string_view getTitle() const override { return FormattedTitle::title(); }
38
39 bool isRelevant(ModControllableAudio* modControllable, int32_t) override {
40 return isSampleModeSample(modControllable, source_id_);
41 }
42
43 bool usesAffectEntire() override { return true; }
44 void readCurrentValue() override {
45 const auto& source = soundEditor.currentSound->sources[source_id_];
46 setValue(source.repeatMode);
47 }
48 void writeCurrentValue() override {
49 const auto current_value = getValue<SampleRepeatMode>();
50
51 Kit* kit = getCurrentKit();
52
53 // If affect-entire button held, do whole kit
54 if (kit != nullptr && currentUIMode == UI_MODE_HOLDING_AFFECT_ENTIRE_IN_SOUND_EDITOR) {
55
56 for (Drum* thisDrum = kit->firstDrum; thisDrum != nullptr; thisDrum = thisDrum->next) {
57 if (thisDrum->type == DrumType::SOUND) {
58 auto* soundDrum = static_cast<SoundDrum*>(thisDrum);
59 Source* source = &soundDrum->sources[source_id_];
60
61 // Automatically switch pitch/speed independence on / off if stretch-to-note-length mode is selected
62 if (current_value == SampleRepeatMode::STRETCH) {
63 soundDrum->killAllVoices();
64 source->sampleControls.pitchAndSpeedAreIndependent = true;
65 }
66 else if (source->repeatMode == SampleRepeatMode::STRETCH) {
67 soundDrum->killAllVoices();
68 source->sampleControls.pitchAndSpeedAreIndependent = false;
69 }
70
71 if (current_value == SampleRepeatMode::ONCE) {
72 // Send note-off for kit arpeggiator to avoid stuck notes
73 sendNoteOffForKitArpeggiator(kit);
74 }
75
76 source->repeatMode = current_value;
77 }
78 }
79 }
80 // Or, the normal case of just one sound
81 else {
82 Source& source = soundEditor.currentSound->sources[source_id_];
83
84 // Automatically switch pitch/speed independence on / off if stretch-to-note-length mode is selected
85 if (current_value == SampleRepeatMode::STRETCH) {
86 soundEditor.currentSound->killAllVoices();
87 source.sampleControls.pitchAndSpeedAreIndependent = true;
88 }
89 else if (source.repeatMode == SampleRepeatMode::STRETCH) {
90 soundEditor.currentSound->killAllVoices();
91 source.sampleControls.pitchAndSpeedAreIndependent = false;
92 }
93
94 if (kit != nullptr && current_value == SampleRepeatMode::ONCE) {
95 // Send note-off for kit arpeggiator to avoid stuck notes
96 sendNoteOffForKitArpeggiator(kit);
97 }
98
99 source.repeatMode = current_value;
100 }
101
102 // We need to re-render all rows, because this will have changed whether Note tails are displayed. Probably just
103 // one row, but we don't know which
104 uiNeedsRendering(&instrumentClipView, 0xFFFFFFFF, 0);
105 }
106 deluge::vector<std::string_view> getOptions(OptType optType) override {
107 (void)optType;
108 return {
109 l10n::getView(l10n::String::STRING_FOR_CUT),
110 l10n::getView(l10n::String::STRING_FOR_ONCE),
111 l10n::getView(l10n::String::STRING_FOR_LOOP),
112 l10n::getView(l10n::String::STRING_FOR_STRETCH),
113 };
114 }
115
116 void renderInHorizontalMenu(int32_t startX, int32_t width, int32_t startY, int32_t height) override {
117 const auto& source = soundEditor.currentSound->sources[source_id_];
118 const Icon& icon = [&] {
119 switch (source.repeatMode) {
120 case SampleRepeatMode::CUT:
121 return OLED::sampleModeCutIcon;
122 case SampleRepeatMode::ONCE:
123 return OLED::sampleModeOnceIcon;
124 case SampleRepeatMode::LOOP:
125 return OLED::sampleModeLoopIcon;
126 case SampleRepeatMode::STRETCH:
127 return OLED::sampleModeStretchIcon;
128 }
129 return OLED::sampleModeCutIcon;
130 }();
131 OLED::main.drawIcon(icon, startX + 4, startY - 1);
132 }
133
134 void getColumnLabel(StringBuf& label) override { label.append(getOptions(OptType::SHORT)[getValue()]); }
135
136private:
137 uint8_t source_id_;
138
139 static void sendNoteOffForKitArpeggiator(Kit* kit) {
140 int32_t noteRowIndex;
141 NoteRow* noteRow = getCurrentInstrumentClip()->getNoteRowForDrum(kit->selectedDrum, &noteRowIndex);
142 char modelStackMemory[MODEL_STACK_MAX_SIZE];
143 ModelStack* modelStack = (ModelStack*)modelStackMemory;
144 ModelStackWithThreeMainThings* modelStackWithThreeMainThings =
145 modelStack->addTimelineCounter(getCurrentClip())
146 ->addNoteRow(noteRowIndex, noteRow)
147 ->addOtherTwoThings(soundEditor.currentModControllable, soundEditor.currentParamManager);
148 kit->noteOffPreKitArp(modelStackWithThreeMainThings, kit->selectedDrum);
149 }
150};
151
152} // namespace deluge::gui::menu_item::sample
Definition drum.h:44
Definition kit.h:34
Definition mod_controllable_audio.h:47
ModelStackWithThreeMainThings * addOtherTwoThings(ModControllable *newModControllable, ParamManager *newParamManager) const
Definition model_stack.h:385
Definition sound_drum.h:28
void killAllVoices() override
Immediately ends all active voices.
Definition sound.cpp:4868
Definition source.h:31
Definition selection.h:26
void readCurrentValue() override
Like readValueAgain, but does not redraw.
Definition repeat.h:44
bool isRelevant(ModControllableAudio *modControllable, int32_t) override
Check if this MenuItem should show up in a containing deluge::gui::menu_item::Submenu.
Definition repeat.h:39
std::string_view getTitle() const override
Get the title to be used when rendering on OLED, both as a deluge::gui::menu_item::Submenu and when d...
Definition repeat.h:37
bool usesAffectEntire() override
Claim support for Kit AFFECT_ENTIRE editing.
Definition repeat.h:43