Deluge Firmware 1.3.0
Build date: 2026.11.02
Loading...
Searching...
No Matches
type.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/selection.h"
21#include "gui/menu_item/submenu.h"
22#include "gui/ui/sound_editor.h"
23#include "model/song/song.h"
24#include "processing/engines/audio_engine.h"
25#include "processing/sound/sound.h"
26#include "processing/source.h"
27#include "util/comparison.h"
28
29#include <hid/display/oled.h>
30
31extern gui::menu_item::Submenu dxMenu;
32
33namespace deluge::gui::menu_item::osc {
34class Type final : public Selection, public FormattedTitle {
35public:
36 Type(l10n::String name, l10n::String title_format_str, uint8_t source_id)
37 : Selection(name), FormattedTitle(title_format_str, source_id + 1), sourceId_{source_id} {};
38 void beginSession(MenuItem* navigatedBackwardFrom) override { Selection::beginSession(navigatedBackwardFrom); }
39
40 bool mayUseDx() const { return sourceId_ == 0; }
41
42 void readCurrentValue() override {
43 int32_t rawVal = static_cast<int32_t>(soundEditor.currentSound->sources[sourceId_].oscType);
44 if (!mayUseDx() && rawVal > static_cast<int32_t>(OscType::DX7)) {
45 rawVal -= 1;
46 }
47 setValue(rawVal);
48 }
49 void writeCurrentValue() override {
50 OscType oldValue = soundEditor.currentSound->sources[sourceId_].oscType;
51 auto newValue = getValue<OscType>();
52 if (!mayUseDx() && static_cast<int32_t>(newValue) >= static_cast<int32_t>(OscType::DX7)) {
53 newValue = static_cast<OscType>(static_cast<int32_t>(newValue) + 1);
54 }
55
56 const auto needs_unassignment = {
57 OscType::INPUT_L,
58 OscType::INPUT_R,
59 OscType::INPUT_STEREO,
60 OscType::SAMPLE,
61 OscType::DX7,
62
63 // Haven't actually really determined if this needs to be here - maybe not?
64 OscType::WAVETABLE,
65 };
66
67 if (util::one_of(oldValue, needs_unassignment) || util::one_of(newValue, needs_unassignment)) {
68 soundEditor.currentSound->killAllVoices();
69 }
70
71 soundEditor.currentSound->sources[sourceId_].setOscType(newValue);
72
73 if (oldValue == OscType::SQUARE || newValue == OscType::SQUARE) {
74 soundEditor.currentSound->setupPatchingForAllParamManagers(currentSong);
75 }
76 }
77
78 [[nodiscard]] std::string_view getTitle() const override { return FormattedTitle::title(); }
79
80 deluge::vector<std::string_view> getOptions(OptType optType) override {
81 (void)optType;
82 using enum l10n::String;
83 deluge::vector options = {
84 l10n::getView(STRING_FOR_SINE), //<
85 l10n::getView(STRING_FOR_TRIANGLE), //<
86 l10n::getView(STRING_FOR_SQUARE), //<
87 l10n::getView(STRING_FOR_ANALOG_SQUARE), //<
88 l10n::getView(STRING_FOR_SAW), //<
89 l10n::getView(STRING_FOR_ANALOG_SAW), //<
90 l10n::getView(STRING_FOR_SUPERSAW), //<
91 l10n::getView(STRING_FOR_WAVETABLE), //<
92 };
93
94 if (soundEditor.currentSound->getSynthMode() == SynthMode::RINGMOD) {
95 return options;
96 }
97
98 options.emplace_back(l10n::getView(STRING_FOR_SAMPLE));
99
100 if (mayUseDx()) {
101 options.emplace_back(l10n::getView(STRING_FOR_DX7));
102 }
103
104 if (AudioEngine::micPluggedIn || AudioEngine::lineInPluggedIn) {
105 options.emplace_back(l10n::getView(STRING_FOR_INPUT_LEFT));
106 options.emplace_back(l10n::getView(STRING_FOR_INPUT_RIGHT));
107 options.emplace_back(l10n::getView(STRING_FOR_INPUT_STEREO));
108 }
109 else {
110 options.emplace_back(l10n::getView(STRING_FOR_INPUT));
111 }
112
113 return options;
114 }
115
116 bool isRelevant(ModControllableAudio* modControllable, int32_t whichThing) const override {
117 Sound* sound = static_cast<Sound*>(modControllable);
118 return (sound->getSynthMode() != SynthMode::FM);
119 }
120
121 MenuItem* selectButtonPress() override {
122 if (soundEditor.currentSound->sources[sourceId_].oscType != OscType::DX7) {
123 return nullptr;
124 }
125 return &dxMenu;
126 }
127
128 [[nodiscard]] bool showColumnLabel() const override { return false; }
129
130 void renderInHorizontalMenu(const SlotPosition& slot) override {
131 oled_canvas::Canvas& image = OLED::main;
132
133 const OscType osc_type = soundEditor.currentSound->sources[sourceId_].oscType;
134 if (osc_type == OscType::DX7) {
135 const auto option = getOptions(OptType::FULL)[getValue()].data();
136 return image.drawStringCentered(option, slot.start_x, slot.start_y + kHorizontalMenuSlotYOffset + 5,
137 kTextTitleSpacingX, kTextTitleSizeY, slot.width);
138 }
139
140 const Icon& icon = [&] {
141 switch (osc_type) {
142 case OscType::SINE:
143 return OLED::sineIcon;
144 case OscType::TRIANGLE:
145 return OLED::triangleIcon;
146 case OscType::SQUARE:
147 case OscType::ANALOG_SQUARE:
148 return OLED::squareIcon;
149 case OscType::SAW:
150 case OscType::ANALOG_SAW_2:
151 case OscType::SUPERSAW:
152 return OLED::sawIcon;
153 case OscType::SAMPLE:
154 return OLED::sampleIcon;
155 case OscType::INPUT_STEREO:
156 case OscType::INPUT_L:
157 case OscType::INPUT_R:
158 return AudioEngine::lineInPluggedIn ? OLED::inputIcon : OLED::micIcon;
159 case OscType::WAVETABLE:
160 return OLED::wavetableIcon;
161 default:
162 return OLED::sineIcon;
163 }
164 }();
165
166 image.drawIconCentered(icon, slot.start_x, slot.width, slot.start_y + kHorizontalMenuSlotYOffset + 2);
167
168 if (osc_type == OscType::ANALOG_SQUARE || osc_type == OscType::ANALOG_SAW_2 || osc_type == OscType::SUPERSAW) {
169 const int32_t x = slot.start_x + 4;
170 constexpr int32_t y = OLED_MAIN_HEIGHT_PIXELS - kTextSpacingY - 8;
171 image.clearAreaExact(x - 1, y - 1, x + kTextSpacingX + 1, y + kTextSpacingY + 1);
172 image.drawChar(osc_type == OscType::SUPERSAW ? 'S' : 'A', x, y, kTextSpacingX, kTextSpacingY);
173 }
174 }
175
176 bool wrapAround() override {
177 return parent != nullptr && parent->renderingStyle() == Submenu::RenderingStyle::HORIZONTAL;
178 }
179
180private:
181 uint8_t sourceId_;
182};
183
184} // namespace deluge::gui::menu_item::osc
const deluge::l10n::String name
Default name for use on OLED for deluge::gui::menu_item::Submenu s.
Definition menu_item.h:260
Definition mod_controllable_audio.h:47
Definition sound.h:76
void beginSession(MenuItem *navigatedBackwardFrom) override
Begin an editing session with this menu item.
Definition enumeration.cpp:7
Definition selection.h:26
Definition submenu.h:28
void beginSession(MenuItem *navigatedBackwardFrom) override
Begin an editing session with this menu item.
Definition type.h:38
bool wrapAround() override
Should this menu wrap around?
Definition type.h:176
MenuItem * selectButtonPress() override
Handle a select button press.
Definition type.h:121
bool showColumnLabel() const override
Show a label for the parameter in Horizontal menu.
Definition type.h:128
void readCurrentValue() override
Like readValueAgain, but does not redraw.
Definition type.h:42
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 type.h:78
bool isRelevant(ModControllableAudio *modControllable, int32_t whichThing) const override
Check if this MenuItem should show up in a containing deluge::gui::menu_item::Submenu.
Definition type.h:116
Definition menu_item.h:35