Deluge Firmware 1.3.0
Build date: 2026.08.29
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) override {
119 audioOutputBeingEdited = (AudioOutput*)getCurrentOutput();
120 return audioOutputBeingEdited->inputChannel == AudioInputChannel::SPECIFIC_OUTPUT;
121 }
122
123 bool shouldEnterSubmenu() override { return true; }
124
125 AudioOutput* audioOutputBeingEdited{nullptr};
126 // this is the index that the output is recording from
127 int32_t outputIndex{0};
128 int32_t numOutputs{0};
129
130private:
131 bool canRecordFromOutput(Output* output) const { return audioOutputBeingEdited->canRecordFrom(output); }
132
133 int32_t getRecordableOutputIndex(Output* output) const {
134 if (!canRecordFromOutput(output)) {
135 return -1;
136 }
137
138 // Only outputs still in the main song list are selectable; hibernated instruments should not display here.
139 int32_t index = 0;
140 for (Output* candidate = currentSong->firstOutput; candidate; candidate = candidate->next) {
141 if (candidate == output) {
142 return index;
143 }
144 index++;
145 }
146
147 return -1;
148 }
149
150 Output* getOutputFromSelectedIndex() const {
151 Output* output = currentSong->getOutputFromIndex(outputIndex);
152 return canRecordFromOutput(output) ? output : nullptr;
153 }
154
155 int32_t getNextRecordableOutputIndex(int32_t startIndex, int32_t offset) const {
156 if (offset == 0) {
157 return getOutputFromSelectedIndex()
158 ? outputIndex
159 : getRecordableOutputIndex(audioOutputBeingEdited->getOutputRecordingFrom());
160 }
161
162 int32_t direction = offset > 0 ? 1 : -1;
163 int32_t steps = offset > 0 ? offset : -offset;
164 int32_t selectedIndex = startIndex;
165
166 for (int32_t step = 0; step < steps; step++) {
167 int32_t candidateIndex = selectedIndex;
168 // Walk the raw song indices, but stop only on rows the audio track can actually record from.
169 while (true) {
170 candidateIndex += direction;
171 if (candidateIndex < 0 || candidateIndex >= numOutputs) {
172 return selectedIndex;
173 }
174
175 Output* candidate = currentSong->getOutputFromIndex(candidateIndex);
176 if (canRecordFromOutput(candidate)) {
177 selectedIndex = candidateIndex;
178 break;
179 }
180 }
181 }
182
183 return selectedIndex;
184 }
185};
186} // 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:228
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:123
void beginSession(MenuItem *navigatedBackwardFrom) override
Begin an editing session with this menu item.
Definition specific_output_source_selector.h:30
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
bool isRelevant(ModControllableAudio *modControllable, int32_t whichThing) override
Check if this MenuItem should show up in a containing deluge::gui::menu_item::Submenu.
Definition specific_output_source_selector.h:118
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:304
int32_t getStringWidthInPixels(char const *string, int32_t textHeight)
Definition canvas.cpp:635
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:383