Deluge Firmware 1.3.0
Build date: 2026.10.14
Loading...
Searching...
No Matches
specific_output_source_selector.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 "gui/context_menu/audio_input_selector.h"
19#include "gui/menu_item/menu_item.h"
20#include "hid/display/display.h"
21#include "hid/display/oled.h"
22#include "model/song/song.h"
23#include "processing/audio_output.h"
24
25namespace deluge::gui::menu_item::audio_clip {
26class SpecificSourceOutputSelector final : public MenuItem {
27public:
28 using MenuItem::MenuItem;
29
30 void beginSession(MenuItem* navigatedBackwardFrom) override {
31 audioOutputBeingEdited = (AudioOutput*)getCurrentOutput();
32 numOutputs = currentSong->getNumOutputs();
33
34 Output* selectedOutput = audioOutputBeingEdited->getOutputRecordingFrom();
35 outputIndex = getRecordableOutputIndex(selectedOutput);
36 if (outputIndex < 0) {
37 // If the stored source was removed or became invalid, land on the first valid target instead.
38 outputIndex = getNextRecordableOutputIndex(-1, 1);
39 selectedOutput = getOutputFromSelectedIndex();
40 audioOutputBeingEdited->setOutputRecordingFrom(selectedOutput);
41 }
42
43 if (outputIndex < 0) {
44 outputIndex = 0;
45 }
46 if (display->haveOLED()) {
47 renderUIsForOled();
48 }
49 else {
50 drawFor7seg(); // Probably not necessary either...
51 }
52 }
53
54 void selectEncoderAction(int32_t offset) override {
55 int32_t newOutputIndex = getNextRecordableOutputIndex(outputIndex, offset);
56 if (newOutputIndex < 0) {
57 return;
58 }
59 outputIndex = newOutputIndex;
60 auto newRecordingFrom = getOutputFromSelectedIndex();
61 audioOutputBeingEdited->setOutputRecordingFrom(newRecordingFrom);
62 if (display->haveOLED()) {
63 renderUIsForOled();
64 }
65 else {
66 drawFor7seg(); // Probably not necessary either...
67 }
68 }
69 void drawPixelsForOled() override {
70 deluge::hid::display::oled_canvas::Canvas& canvas = hid::display::OLED::main;
71
72 // track
73 Output* output = getOutputFromSelectedIndex();
74 if (!output) {
75 canvas.drawStringCentred("No track", OLED_MAIN_TOPMOST_PIXEL + 21, kTextSpacingX, kTextSpacingY);
76 return;
77 }
78
79 // track type
80 OutputType outputType = output->type;
81
82 // for midi instruments, get the channel
83 int32_t channel = 0;
84 if (outputType == OutputType::MIDI_OUT) {
85 Instrument* instrument = (Instrument*)output;
86 channel = ((NonAudioInstrument*)instrument)->getChannel();
87 }
88
89 char const* outputTypeText = getOutputTypeName(outputType, channel);
90
91 // draw the track type
92 canvas.drawStringCentred(outputTypeText, OLED_MAIN_TOPMOST_PIXEL + 14, kTextSpacingX, kTextSpacingY);
93
94 int32_t yPos = OLED_MAIN_TOPMOST_PIXEL + 28;
95
96 // draw the track name
97 char const* name = output->name.get();
98
99 int32_t stringLengthPixels = canvas.getStringWidthInPixels(name, kTextTitleSizeY);
100
101 if (stringLengthPixels <= OLED_MAIN_WIDTH_PIXELS) {
102 canvas.drawStringCentred(name, yPos, kTextTitleSpacingX, kTextTitleSizeY);
103 }
104 else {
105 canvas.drawString(name, 0, yPos, kTextTitleSpacingX, kTextTitleSizeY);
106 deluge::hid::display::OLED::setupSideScroller(0, name, 0, OLED_MAIN_WIDTH_PIXELS, yPos,
107 yPos + kTextTitleSizeY, kTextTitleSpacingX, kTextTitleSizeY,
108 false);
109 }
110 }
111
112 void drawFor7seg() {
113 Output* output = getOutputFromSelectedIndex();
114 char const* text = output ? output->name.get() : "No track";
115 display->setScrollingText(text, 0);
116 }
117
118 bool isRelevant(ModControllableAudio* modControllable, int32_t whichThing) const override {
119 auto* output = getCurrentOutput();
120 return output and output->type == OutputType::AUDIO
121 and ((AudioOutput*)output)->inputChannel == AudioInputChannel::SPECIFIC_OUTPUT;
122 }
123
124 bool shouldEnterSubmenu() override { return true; }
125
126 AudioOutput* audioOutputBeingEdited{nullptr};
127 // this is the index that the output is recording from
128 int32_t outputIndex{0};
129 int32_t numOutputs{0};
130
131private:
132 bool canRecordFromOutput(Output* output) const { return audioOutputBeingEdited->canRecordFrom(output); }
133
134 int32_t getRecordableOutputIndex(Output* output) const {
135 if (!canRecordFromOutput(output)) {
136 return -1;
137 }
138
139 // Only outputs still in the main song list are selectable; hibernated instruments should not display here.
140 int32_t index = 0;
141 for (Output* candidate = currentSong->firstOutput; candidate; candidate = candidate->next) {
142 if (candidate == output) {
143 return index;
144 }
145 index++;
146 }
147
148 return -1;
149 }
150
151 Output* getOutputFromSelectedIndex() const {
152 Output* output = currentSong->getOutputFromIndex(outputIndex);
153 return canRecordFromOutput(output) ? output : nullptr;
154 }
155
156 int32_t getNextRecordableOutputIndex(int32_t startIndex, int32_t offset) const {
157 if (offset == 0) {
158 return getOutputFromSelectedIndex()
159 ? outputIndex
160 : getRecordableOutputIndex(audioOutputBeingEdited->getOutputRecordingFrom());
161 }
162
163 int32_t direction = offset > 0 ? 1 : -1;
164 int32_t steps = offset > 0 ? offset : -offset;
165 int32_t selectedIndex = startIndex;
166
167 for (int32_t step = 0; step < steps; step++) {
168 int32_t candidateIndex = selectedIndex;
169 // Walk the raw song indices, but stop only on rows the audio track can actually record from.
170 while (true) {
171 candidateIndex += direction;
172 if (candidateIndex < 0 || candidateIndex >= numOutputs) {
173 return selectedIndex;
174 }
175
176 Output* candidate = currentSong->getOutputFromIndex(candidateIndex);
177 if (canRecordFromOutput(candidate)) {
178 selectedIndex = candidateIndex;
179 break;
180 }
181 }
182 }
183
184 return selectedIndex;
185 }
186};
187} // namespace deluge::gui::menu_item::audio_clip
Definition audio_output.h:37
Definition instrument.h:45
const deluge::l10n::String name
Default name for use on OLED for deluge::gui::menu_item::Submenu s.
Definition menu_item.h:253
Definition mod_controllable_audio.h:47
Definition non_audio_instrument.h:27
Definition output.h:81
virtual Output * getOutputRecordingFrom()
Which output this one records from, or nullptr. Only audio outputs ever record from another output.
Definition output.h:229
Definition specific_output_source_selector.h:26
bool shouldEnterSubmenu() override
Check if selecting this menu item (with select encoder) should enter a submenu.
Definition specific_output_source_selector.h:124
void beginSession(MenuItem *navigatedBackwardFrom) override
Begin an editing session with this menu item.
Definition specific_output_source_selector.h:30
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 specific_output_source_selector.h:118
void selectEncoderAction(int32_t offset) override
Handle select encoder movement.
Definition specific_output_source_selector.h:54
void drawPixelsForOled() override
Paints the pixels below the standard title block.
Definition specific_output_source_selector.h:69
void drawString(std::string_view str, int32_t pixelX, int32_t pixelY, int32_t textWidth, int32_t textHeight, int32_t scrollPos=0, int32_t endX=OLED_MAIN_WIDTH_PIXELS, bool useTextWidth=false)
Definition canvas.cpp:307
int32_t getStringWidthInPixels(char const *string, int32_t textHeight)
Definition canvas.cpp:766
void drawStringCentred(char const *string, int32_t pixelY, int32_t textWidth, int32_t textHeight, int32_t centrePos=OLED_MAIN_WIDTH_PIXELS/2)
Definition canvas.cpp:404