Deluge Firmware 1.3.0
Build date: 2025.10.22
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 !soundEditor.editingKit() && 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_WAVETABLE), //<
91 };
92
93 if (soundEditor.currentSound->getSynthMode() == SynthMode::RINGMOD) {
94 return options;
95 }
96
97 options.emplace_back(l10n::getView(STRING_FOR_SAMPLE));
98
99 if (mayUseDx()) {
100 options.emplace_back(l10n::getView(STRING_FOR_DX7));
101 }
102
103 if (AudioEngine::micPluggedIn || AudioEngine::lineInPluggedIn) {
104 options.emplace_back(l10n::getView(STRING_FOR_INPUT_LEFT));
105 options.emplace_back(l10n::getView(STRING_FOR_INPUT_RIGHT));
106 options.emplace_back(l10n::getView(STRING_FOR_INPUT_STEREO));
107 }
108 else {
109 options.emplace_back(l10n::getView(STRING_FOR_INPUT));
110 }
111
112 return options;
113 }
114
115 bool isRelevant(ModControllableAudio* modControllable, int32_t whichThing) override {
116 Sound* sound = static_cast<Sound*>(modControllable);
117 return (sound->getSynthMode() != SynthMode::FM);
118 }
119
120 MenuItem* selectButtonPress() override {
121 if (soundEditor.currentSound->sources[sourceId_].oscType != OscType::DX7) {
122 return nullptr;
123 }
124 return &dxMenu;
125 }
126
127 [[nodiscard]] bool showColumnLabel() const override { return false; }
128
129 void renderInHorizontalMenu(int32_t startX, int32_t width, int32_t startY, int32_t height) override {
130 oled_canvas::Canvas& image = OLED::main;
131
132 const OscType oscType = soundEditor.currentSound->sources[sourceId_].oscType;
133 if (oscType == OscType::DX7) {
134 const auto option = getOptions(OptType::FULL)[getValue()].data();
135 return image.drawStringCentered(option, startX, startY + kHorizontalMenuSlotYOffset + 5, kTextTitleSpacingX,
136 kTextTitleSizeY, width);
137 }
138
139 const Icon& icon = [&] {
140 switch (oscType) {
141 case OscType::SINE:
142 return OLED::sineIcon;
143 case OscType::TRIANGLE:
144 return OLED::triangleIcon;
145 case OscType::SQUARE:
146 case OscType::ANALOG_SQUARE:
147 return OLED::squareIcon;
148 case OscType::SAW:
149 case OscType::ANALOG_SAW_2:
150 return OLED::sawIcon;
151 case OscType::SAMPLE:
152 return OLED::sampleIcon;
153 case OscType::INPUT_STEREO:
154 case OscType::INPUT_L:
155 case OscType::INPUT_R:
156 return AudioEngine::lineInPluggedIn ? OLED::inputIcon : OLED::micIcon;
157 case OscType::WAVETABLE:
158 return OLED::wavetableIcon;
159 default:
160 return OLED::sineIcon;
161 }
162 }();
163
164 image.drawIconCentered(icon, startX, width, startY + kHorizontalMenuSlotYOffset + 2);
165
166 if (oscType == OscType::ANALOG_SQUARE || oscType == OscType::ANALOG_SAW_2) {
167 const int32_t x = startX + 4;
168 constexpr int32_t y = OLED_MAIN_HEIGHT_PIXELS - kTextSpacingY - 8;
169 image.clearAreaExact(x - 1, y - 1, x + kTextSpacingX + 1, y + kTextSpacingY + 1);
170 image.drawChar('A', x, y, kTextSpacingX, kTextSpacingY);
171 }
172 }
173
174 bool wrapAround() override {
175 return parent != nullptr && parent->renderingStyle() == Submenu::RenderingStyle::HORIZONTAL;
176 }
177
178private:
179 uint8_t sourceId_;
180};
181
182} // namespace deluge::gui::menu_item::osc
Definition mod_controllable_audio.h:47
Definition sound.h:71
void beginSession(MenuItem *navigatedBackwardFrom) override
Begin an editing session with this menu item.
Definition enumeration.cpp:8
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:174
MenuItem * selectButtonPress() override
Handle a select button press.
Definition type.h:120
bool showColumnLabel() const override
Show a label for the parameter in Horizontal menu.
Definition type.h:127
void readCurrentValue() override
Like readValueAgain, but does not redraw.
Definition type.h:42
bool isRelevant(ModControllableAudio *modControllable, int32_t whichThing) override
Check if this MenuItem should show up in a containing deluge::gui::menu_item::Submenu.
Definition type.h:115
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