Mema
Memory Matrix — multi-channel audio matrix monitor and router
Loading...
Searching...
No Matches
FaderbankControlComponent.cpp
Go to the documentation of this file.
1/* Copyright (c) 2025, Christian Ahrens
2 *
3 * This file is part of Mema <https://github.com/ChristianAhrens/Mema>
4 *
5 * This tool is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License version 3.0 as published
7 * by the Free Software Foundation.
8 *
9 * This tool is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 * details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this tool; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
20
21#include <CustomLookAndFeel.h>
22#include <ToggleStateSlider.h>
24
25
26namespace Mema
27{
28
29
32{
33 m_horizontalScrollContainerComponent = std::make_unique<juce::Component>();
34 m_horizontalScrollViewport = std::make_unique<juce::Viewport>();
35 m_horizontalScrollViewport->setViewedComponent(m_horizontalScrollContainerComponent.get(), false);
36 addAndMakeVisible(m_horizontalScrollViewport.get());
37 m_verticalScrollContainerComponent = std::make_unique<juce::Component>();
38 m_verticalScrollViewport = std::make_unique<juce::Viewport>();
39 m_verticalScrollViewport->setViewedComponent(m_verticalScrollContainerComponent.get(), false);
40 addAndMakeVisible(m_verticalScrollViewport.get());
41 m_hvScrollContainerComponent = std::make_unique<juce::Component>();
42 m_hvScrollViewport = std::make_unique<juce::Viewport>();
43 m_hvScrollViewport->setViewedComponent(m_hvScrollContainerComponent.get(), false);
44 addAndMakeVisible(m_hvScrollViewport.get());
45
46 m_inputControlsGrid = std::make_unique<juce::Grid>();
47 m_inputControlsGrid->setGap(juce::Grid::Px(s_gap));
48
49 m_outputControlsGrid = std::make_unique<juce::Grid>();
50 m_outputControlsGrid->setGap(juce::Grid::Px(s_gap));
51
52 m_crosspointsControlsGrid = std::make_unique<juce::Grid>();
53 m_crosspointsControlsGrid->setGap(juce::Grid::Px(s_gap));
54}
55
59
60void FaderbankControlComponent::paint(juce::Graphics& g)
61{
62 auto ctrlsSize = 2 * (m_controlsSize + s_gap);
63
64 // crosspointsControls body background
65 g.setColour(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
66 g.fillRect(getLocalBounds().removeFromBottom(getHeight() - ctrlsSize).removeFromRight(getWidth() - ctrlsSize));
67
68 // horizontal and vertical controls background
69 g.setColour(getLookAndFeel().findColour(juce::Slider::backgroundColourId));
70 g.fillRect(getLocalBounds().removeFromTop(ctrlsSize + s_scrollbarsize).removeFromRight(getWidth() - ctrlsSize - s_scrollbarsize));
71 g.fillRect(getLocalBounds().removeFromLeft(ctrlsSize + s_scrollbarsize).removeFromBottom(getHeight() - ctrlsSize - s_scrollbarsize));
72
73 // upper-left controls rectangle background
74 g.setColour(getLookAndFeel().findColour(juce::Slider::backgroundColourId));
75 g.fillRect(juce::Rectangle<int>(0, 0, ctrlsSize + s_scrollbarsize, ctrlsSize + s_scrollbarsize));
76 g.setColour(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
77 g.fillRect(juce::Rectangle<int>(0, 0, ctrlsSize, ctrlsSize));
78}
79
81{
82 auto ctrlsSize = 2 * (m_controlsSize + s_gap) + s_scrollbarsize;
83 auto currentInputsWidth = (m_inputControlsGrid->getNumberOfColumns() * (m_controlsSize + s_gap)) - s_gap;
84 auto currentOutputsHeight = (m_outputControlsGrid->getNumberOfRows() * (m_controlsSize + s_gap)) - s_gap;
85 auto crosspointControlBounds = getLocalBounds();
86
87 if (m_inputControlsGrid)
88 {
89 if (ControlDirection::None == m_currentIOChannel.first)
90 m_inputControlsGrid->performLayout({ ctrlsSize, 0, currentInputsWidth, ctrlsSize - s_scrollbarsize });
91 else
92 m_inputControlsGrid->performLayout({ 0, 0, currentInputsWidth, ctrlsSize - s_scrollbarsize });
93 }
94
95 if (m_outputControlsGrid)
96 {
97 if (ControlDirection::None == m_currentIOChannel.first)
98 m_outputControlsGrid->performLayout({ 0, ctrlsSize, ctrlsSize - s_scrollbarsize, currentOutputsHeight });
99 else
100 m_outputControlsGrid->performLayout({ 0, 0, ctrlsSize - s_scrollbarsize, currentOutputsHeight });
101 }
102
103 if (m_crosspointsControlsGrid)
104 {
105 if (ControlDirection::Input == m_currentIOChannel.first)
106 {
107 crosspointControlBounds.removeFromLeft(ctrlsSize);
108 crosspointControlBounds.removeFromRight(s_gap);
109 }
110 else if (ControlDirection::Output == m_currentIOChannel.first)
111 {
112 crosspointControlBounds.removeFromTop(ctrlsSize);
113 crosspointControlBounds.removeFromBottom(s_gap);
114 }
115 else if (ControlDirection::None == m_currentIOChannel.first)
116 {
117 crosspointControlBounds.removeFromLeft(ctrlsSize);
118 crosspointControlBounds.removeFromRight(s_gap);
119 crosspointControlBounds.removeFromTop(ctrlsSize);
120 crosspointControlBounds.removeFromBottom(s_gap);
121 }
122 m_crosspointsControlsGrid->performLayout(crosspointControlBounds);
123 }
124
125 if (ControlDirection::Input == m_currentIOChannel.first)
126 {
127 addAndMakeVisible(m_horizontalScrollViewport.get());
128 addAndMakeVisible(m_verticalScrollViewport.get());
129 if (m_hvScrollViewport) m_hvScrollViewport->setVisible(false);
130
131 m_horizontalScrollContainerComponent->setBounds({ 0, 0, currentInputsWidth, ctrlsSize - s_scrollbarsize });
132 m_horizontalScrollViewport->setBounds(getLocalBounds().removeFromTop(ctrlsSize).removeFromRight(getWidth() - ctrlsSize));
133 m_verticalScrollContainerComponent->setBounds({ 0, 0, getWidth() - s_scrollbarsize, currentOutputsHeight });
134 m_verticalScrollViewport->setBounds(getLocalBounds().removeFromBottom(getHeight() - ctrlsSize));
135 }
136 else if (ControlDirection::Output == m_currentIOChannel.first)
137 {
138 addAndMakeVisible(m_horizontalScrollViewport.get());
139 addAndMakeVisible(m_verticalScrollViewport.get());
140 if (m_hvScrollViewport) m_hvScrollViewport->setVisible(false);
141
142 m_horizontalScrollContainerComponent->setBounds({ 0, 0, currentInputsWidth, getHeight() - s_scrollbarsize });
143 m_horizontalScrollViewport->setBounds(getLocalBounds().removeFromRight(getWidth() - ctrlsSize));
144 m_verticalScrollContainerComponent->setBounds({ 0, 0, ctrlsSize - s_scrollbarsize, currentOutputsHeight });
145 m_verticalScrollViewport->setBounds(getLocalBounds().removeFromBottom(getHeight() - ctrlsSize));
146 }
147 else if (ControlDirection::None == m_currentIOChannel.first)
148 {
149 if (m_horizontalScrollViewport) m_horizontalScrollViewport->setVisible(false);
150 if (m_verticalScrollViewport) m_verticalScrollViewport->setVisible(false);
151 addAndMakeVisible(m_hvScrollViewport.get());
152
153 m_hvScrollContainerComponent->setBounds({ 0, 0, currentInputsWidth + ctrlsSize, currentOutputsHeight + ctrlsSize });
154 m_hvScrollViewport->setBounds(getLocalBounds());
155 }
156}
157
159{
160 auto ioCount = getIOCount();
161 for (auto in = 0; in < ioCount.first; in++)
162 {
163 if (nullptr != m_inputSelectButtons.at(in))
164 m_inputSelectButtons.at(in)->setColour(juce::TextButton::ColourIds::buttonOnColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
165 if (ControlDirection::Output == m_currentIOChannel.first && m_crosspointGainSliders.size() > in && nullptr != m_crosspointGainSliders.at(in))
166 m_crosspointGainSliders.at(in)->setColour(juce::Slider::ColourIds::trackColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
167 }
168 for (auto out = 0; out < ioCount.second; out++)
169 {
170 if (nullptr != m_outputSelectButtons.at(out))
171 m_outputSelectButtons.at(out)->setColour(juce::TextButton::ColourIds::buttonOnColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
172 if (ControlDirection::Input == m_currentIOChannel.first && m_crosspointGainSliders.size() > out && nullptr != m_crosspointGainSliders.at(out))
173 m_crosspointGainSliders.at(out)->setColour(juce::Slider::ColourIds::trackColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
174 }
175}
176
178{
180
181 rebuildControls(true);
182 selectIOChannel(m_currentIOChannel.first, m_currentIOChannel.second);
183}
184
190
191void FaderbankControlComponent::setIOCount(const std::pair<int, int>& ioCount)
192{
194
196 selectIOChannel(m_currentIOChannel.first, m_currentIOChannel.second);
197}
198
206
208{
209 auto ioCount = getIOCount();
210
211 if (ioCount.first != m_inputSelectButtons.size() || ioCount.first != m_inputMuteButtons.size() || force)
212 {
213 auto templateColums = juce::Array<juce::Grid::TrackInfo>();
214 for (auto in = 0; in < ioCount.first; in++)
215 templateColums.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
216
217 m_inputMuteButtons.resize(ioCount.first);
218 m_inputSelectButtons.resize(ioCount.first);
219 m_inputControlsGrid->items.clear();
220 m_inputControlsGrid->templateRows = { juce::Grid::TrackInfo(juce::Grid::Fr(1)), juce::Grid::TrackInfo(juce::Grid::Fr(1)) };
221 m_inputControlsGrid->templateColumns = templateColums;
222
223 for (auto i = 0; i < ioCount.first; i++)
224 {
225 auto in = std::uint16_t(i + 1);
226 m_inputSelectButtons.at(i) = std::make_unique<juce::TextButton>("In " + juce::String(in));
227 m_inputSelectButtons.at(i)->setClickingTogglesState(true);
228 m_inputSelectButtons.at(i)->setColour(juce::TextButton::ColourIds::buttonOnColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
229 m_inputSelectButtons.at(i)->onClick = [this, i] {
230 if (m_inputSelectButtons.size() > i)
231 {
232 auto in = std::uint16_t(i + 1);
233 if (m_inputSelectButtons.at(i)->getToggleState())
235 else
237 }
238 else
239 jassertfalse;
240 };
241 if (ControlDirection::None == m_currentIOChannel.first)
242 m_hvScrollContainerComponent->addAndMakeVisible(m_inputSelectButtons.at(i).get());
243 else
244 m_horizontalScrollContainerComponent->addAndMakeVisible(m_inputSelectButtons.at(i).get());
245 m_inputControlsGrid->items.add(juce::GridItem(m_inputSelectButtons.at(i).get()));
246 }
247 for (auto i = 0; i < ioCount.first; i++)
248 {
249 auto in = std::uint16_t(i + 1);
250 m_inputMuteButtons.at(i) = std::make_unique<juce::TextButton>("M");
251 m_inputMuteButtons.at(i)->setClickingTogglesState(true);
252 m_inputMuteButtons.at(i)->setToggleState((getInputMuteStates().count(in) != 0 ? getInputMuteStates().at(in) : false), juce::dontSendNotification);
253 m_inputMuteButtons.at(i)->setColour(juce::TextButton::ColourIds::buttonOnColourId, juce::Colours::red);
254 m_inputMuteButtons.at(i)->onClick = [this, i] {
255 auto inputMuteStates = std::map<std::uint16_t, bool>();
256 auto in = std::uint16_t(i + 1);
257 inputMuteStates[in] = m_inputMuteButtons.at(i)->getToggleState();
260 onInputMutesChanged(inputMuteStates);
261 };
262 if (ControlDirection::None == m_currentIOChannel.first)
263 m_hvScrollContainerComponent->addAndMakeVisible(m_inputMuteButtons.at(i).get());
264 else
265 m_horizontalScrollContainerComponent->addAndMakeVisible(m_inputMuteButtons.at(i).get());
266 m_inputControlsGrid->items.add(juce::GridItem(m_inputMuteButtons.at(i).get()));
267 }
268 }
269}
270
272{
273 auto ioCount = getIOCount();
274
275 if (ioCount.second != m_outputSelectButtons.size() || ioCount.second != m_outputMuteButtons.size() || force)
276 {
277 auto templateRows = juce::Array<juce::Grid::TrackInfo>();
278 for (auto out = 0; out < ioCount.second; out++)
279 templateRows.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
280
281 m_outputMuteButtons.resize(ioCount.second);
282 m_outputSelectButtons.resize(ioCount.second);
283 m_outputControlsGrid->items.clear();
284 m_outputControlsGrid->templateRows = templateRows;
285 m_outputControlsGrid->templateColumns = { juce::Grid::TrackInfo(juce::Grid::Fr(1)), juce::Grid::TrackInfo(juce::Grid::Fr(1)) };
286
287 for (auto o = 0; o < ioCount.second; o++)
288 {
289 auto out = std::uint16_t(o + 1);
290 m_outputSelectButtons.at(o) = std::make_unique<juce::TextButton>("Out " + juce::String(out));
291 m_outputSelectButtons.at(o)->setClickingTogglesState(true);
292 m_outputSelectButtons.at(o)->setColour(juce::TextButton::ColourIds::buttonOnColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
293 m_outputSelectButtons.at(o)->onClick = [this, o] {
294 if (m_outputSelectButtons.size() > o)
295 {
296 auto out = std::uint16_t(o + 1);
297 if (m_outputSelectButtons.at(o)->getToggleState())
299 else
301 }
302 else
303 jassertfalse;
304 };
305 if (ControlDirection::None == m_currentIOChannel.first)
306 m_hvScrollContainerComponent->addAndMakeVisible(m_outputSelectButtons.at(o).get());
307 else
308 m_verticalScrollContainerComponent->addAndMakeVisible(m_outputSelectButtons.at(o).get());
309 m_outputControlsGrid->items.add(juce::GridItem(m_outputSelectButtons.at(o).get()));
310
311 m_outputMuteButtons.at(o) = std::make_unique<juce::TextButton>("M");
312 m_outputMuteButtons.at(o)->setClickingTogglesState(true);
313 m_outputMuteButtons.at(o)->setToggleState((getOutputMuteStates().count(out) != 0 ? getOutputMuteStates().at(out) : false), juce::dontSendNotification);
314 m_outputMuteButtons.at(o)->setColour(juce::TextButton::ColourIds::buttonOnColourId, juce::Colours::red);
315 m_outputMuteButtons.at(o)->onClick = [this, o] {
316 auto outputMuteStates = std::map<std::uint16_t, bool>();
317 auto out = std::uint16_t(o + 1);
318 outputMuteStates[out] = m_outputMuteButtons.at(o)->getToggleState();
321 onOutputMutesChanged(outputMuteStates);
322 };
323 if (ControlDirection::None == m_currentIOChannel.first)
324 m_hvScrollContainerComponent->addAndMakeVisible(m_outputMuteButtons.at(o).get());
325 else
326 m_verticalScrollContainerComponent->addAndMakeVisible(m_outputMuteButtons.at(o).get());
327 m_outputControlsGrid->items.add(juce::GridItem(m_outputMuteButtons.at(o).get()));
328 }
329 }
330}
331
333{
334 auto ioCount = getIOCount();
335
336 if (ControlDirection::Output == m_currentIOChannel.first)
337 {
338 if (force || ioCount.first != m_crosspointGainSliders.size())
339 {
340 auto templateColums = juce::Array<juce::Grid::TrackInfo>();
341 for (auto in = 0; in < ioCount.first; in++)
342 templateColums.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
343
344 m_crosspointGainSliders.resize(ioCount.first);
345 m_crosspointsControlsGrid->items.clear();
346 m_crosspointsControlsGrid->templateRows = { juce::Grid::TrackInfo(juce::Grid::Fr(1)) };
347 m_crosspointsControlsGrid->templateColumns = templateColums;
348
349 for (auto i = 0; i < ioCount.first; i++)
350 {
351 m_crosspointGainSliders.at(i) = std::make_unique<JUCEAppBasics::ToggleStateSlider>(juce::Slider::LinearVertical, juce::Slider::NoTextBox);
352 m_crosspointGainSliders.at(i)->setColour(juce::Slider::ColourIds::trackColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
353 m_crosspointGainSliders.at(i)->setRange(0.0, 1.0, 0.01);
354 m_crosspointGainSliders.at(i)->setTitle(juce::String(i + 1));
355 m_crosspointGainSliders.at(i)->displayValueConverter = [](double val) { return juce::String(juce::Decibels::gainToDecibels(val, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), 1) + " dB"; };
356 //juce::Decibels::gainToDecibels(0.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
357 //juce::Decibels::gainToDecibels(1.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
358 //0.1);
359 m_crosspointGainSliders.at(i)->onValueChange = [this, i] {
360 auto crosspointValues = std::map<std::uint16_t, std::map<std::uint16_t, float>>();
361 //auto faderValue = juce::Decibels::decibelsToGain(m_crosspointGainSliders.at(i)->getValue(), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB()));
362 auto faderValue = m_crosspointGainSliders.at(i)->getValue();
363 auto in = std::uint16_t(i + 1);
364 auto out = std::uint16_t(m_currentIOChannel.second);
365 crosspointValues[in][out] = float(faderValue);
367 onCrosspointValuesChanged(crosspointValues);
368 addCrosspointValues(crosspointValues);
369 };
370 m_crosspointGainSliders.at(i)->onToggleStateChange = [this, i] {
371 auto crosspointStates = std::map<std::uint16_t, std::map<std::uint16_t, bool>>();
372 //auto faderValue = juce::Decibels::decibelsToGain(m_crosspointGainSliders.at(i)->getValue(), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB()));
373 auto faderState = m_crosspointGainSliders.at(i)->getToggleState();
374 auto in = std::uint16_t(i + 1);
375 auto out = std::uint16_t(m_currentIOChannel.second);
376 crosspointStates[in][out] = faderState;
378 onCrosspointStatesChanged(crosspointStates);
379 addCrosspointStates(crosspointStates);
380 };
381 m_horizontalScrollContainerComponent->addAndMakeVisible(m_crosspointGainSliders.at(i).get());
382 m_crosspointsControlsGrid->items.add(juce::GridItem(m_crosspointGainSliders.at(i).get()));
383 }
384 }
385 for (auto i = 0; i < ioCount.first; i++)
386 {
387 if (m_crosspointGainSliders.size() > i)
388 {
389 auto in = std::uint16_t(i + 1);
390 auto out = std::uint16_t(m_currentIOChannel.second);
391 auto crosspointState = (getCrosspointStates().count(in) != 0 && getCrosspointStates().at(in).count(out) != 0) ? getCrosspointStates().at(in).at(out) : false;
392 auto crosspointValue = (getCrosspointValues().count(in) != 0 && getCrosspointValues().at(in).count(out) != 0) ? getCrosspointValues().at(in).at(out) : 0.0f;
393 m_crosspointGainSliders.at(i)->setToggleState(crosspointState, juce::dontSendNotification);
394 //m_crosspointGainSliders.at(i)->setValue(juce::Decibels::gainToDecibels(double(crosspointState.second), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), juce::dontSendNotification);
395 m_crosspointGainSliders.at(i)->setValue(double(crosspointValue), juce::dontSendNotification);
396 m_crosspointGainSliders.at(i)->setVisible(true);
397 }
398 }
399 }
400 else if (ControlDirection::Input == m_currentIOChannel.first)
401 {
402 if (force || ioCount.second != m_crosspointGainSliders.size())
403 {
404 auto templateRows = juce::Array<juce::Grid::TrackInfo>();
405 for (auto out = 0; out < ioCount.second; out++)
406 templateRows.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
407
408 m_crosspointGainSliders.resize(ioCount.second);
409 m_crosspointsControlsGrid->items.clear();
410 m_crosspointsControlsGrid->templateColumns = { juce::Grid::TrackInfo(juce::Grid::Fr(1)) };
411 m_crosspointsControlsGrid->templateRows = templateRows;
412
413 for (auto o = 0; o < ioCount.second; o++)
414 {
415 m_crosspointGainSliders.at(o) = std::make_unique<JUCEAppBasics::ToggleStateSlider>(juce::Slider::LinearHorizontal, juce::Slider::NoTextBox);
416 m_crosspointGainSliders.at(o)->setColour(juce::Slider::ColourIds::trackColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
417 m_crosspointGainSliders.at(o)->setRange(0.0, 1.0, 0.01);
418 m_crosspointGainSliders.at(o)->setTitle(juce::String(o + 1));
419 m_crosspointGainSliders.at(o)->displayValueConverter = [](double val) { return juce::String(juce::Decibels::gainToDecibels(val, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), 1) + " dB"; };
420 //juce::Decibels::gainToDecibels(0.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
421 //juce::Decibels::gainToDecibels(1.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
422 //0.1);
423 m_crosspointGainSliders.at(o)->onValueChange = [this, o] {
424 auto crosspointValues = std::map<std::uint16_t, std::map<std::uint16_t, float>>();
425 //auto faderValue = juce::Decibels::decibelsToGain(m_crosspointGainSliders.at(o)->getValue(), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB()));
426 auto faderValue = m_crosspointGainSliders.at(o)->getValue();
427 auto in = std::uint16_t(m_currentIOChannel.second);
428 auto out = std::uint16_t(o + 1);
429 crosspointValues[in][out] = float(faderValue);
431 onCrosspointValuesChanged(crosspointValues);
432 addCrosspointValues(crosspointValues);
433 };
434 m_crosspointGainSliders.at(o)->onToggleStateChange = [this, o] {
435 auto crosspointStates = std::map<std::uint16_t, std::map<std::uint16_t, bool>>();
436 auto faderState = m_crosspointGainSliders.at(o)->getToggleState();
437 auto in = std::uint16_t(m_currentIOChannel.second);
438 auto out = std::uint16_t(o + 1);
439 crosspointStates[in][out] = faderState;
441 onCrosspointStatesChanged(crosspointStates);
442 addCrosspointStates(crosspointStates);
443 };
444 m_verticalScrollContainerComponent->addAndMakeVisible(m_crosspointGainSliders.at(o).get());
445 m_crosspointsControlsGrid->items.add(juce::GridItem(m_crosspointGainSliders.at(o).get()));
446
447 }
448 }
449 for (auto o = 0; o < ioCount.second; o++)
450 {
451 if (m_crosspointGainSliders.size() > o)
452 {
453 auto in = std::uint16_t(m_currentIOChannel.second);
454 auto out = std::uint16_t(o + 1);
455 auto crosspointState = ((getCrosspointStates().count(in) != 0) != 0 && getCrosspointStates().at(in).count(out) != 0) ? getCrosspointStates().at(in).at(out) : false;
456 auto crosspointValue = ((getCrosspointValues().count(in) != 0) != 0 && getCrosspointValues().at(in).count(out) != 0) ? getCrosspointValues().at(in).at(out) : 0.0f;
457 m_crosspointGainSliders.at(o)->setToggleState(crosspointState, juce::dontSendNotification);
458 //m_crosspointGainSliders.at(o)->setValue(juce::Decibels::gainToDecibels(double(crosspointState.second), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), juce::dontSendNotification);
459 m_crosspointGainSliders.at(o)->setValue(double(crosspointValue), juce::dontSendNotification);
460 m_crosspointGainSliders.at(o)->setVisible(true);
461 }
462 }
463 }
464 else if (ControlDirection::None == m_currentIOChannel.first)
465 {
466 if (force || (ioCount.first * ioCount.second) != m_crosspointGainSliders.size())
467 {
468 auto templateColumns = juce::Array<juce::Grid::TrackInfo>();
469 for (auto in = 0; in < ioCount.first; in++)
470 templateColumns.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
471 auto templateRows = juce::Array<juce::Grid::TrackInfo>();
472 for (auto out = 0; out < ioCount.second; out++)
473 templateRows.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
474
475 m_crosspointGainSliders.resize(ioCount.first * ioCount.second);
476 m_crosspointsControlsGrid->items.clear();
477 m_crosspointsControlsGrid->templateColumns = templateColumns;
478 m_crosspointsControlsGrid->templateRows = templateRows;
479
480 for (auto o = 0; o < ioCount.second; o++)
481 {
482 for (auto i = 0; i < ioCount.first; i++)
483 {
484 auto idx = o + i * ioCount.second;
485 m_crosspointGainSliders.at(idx) = std::make_unique<JUCEAppBasics::ToggleStateSlider>(juce::Slider::Rotary, juce::Slider::NoTextBox);
486 m_crosspointGainSliders.at(idx)->setColour(juce::Slider::ColourIds::trackColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
487 m_crosspointGainSliders.at(idx)->setRange(0.0, 1.0, 0.01);
488 m_crosspointGainSliders.at(idx)->displayValueConverter = [](double val) { return juce::String(juce::Decibels::gainToDecibels(val, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), 1) + " dB"; };
489 //juce::Decibels::gainToDecibels(0.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
490 //juce::Decibels::gainToDecibels(1.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
491 //0.1);
492 m_crosspointGainSliders.at(idx)->onValueChange = [this, idx, i, o] {
493 auto crosspointValues = std::map<std::uint16_t, std::map<std::uint16_t, float>>();
494 //auto faderValue = juce::Decibels::decibelsToGain(m_crosspointGainSliders.at(o)->getValue(), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB()));
495 auto faderValue = m_crosspointGainSliders.at(idx)->getValue();
496 auto in = std::uint16_t(i + 1);
497 auto out = std::uint16_t(o + 1);
498 crosspointValues[in][out] = float(faderValue);
500 onCrosspointValuesChanged(crosspointValues);
501 addCrosspointValues(crosspointValues);
502 };
503 m_crosspointGainSliders.at(idx)->onToggleStateChange = [this, idx, i, o] {
504 auto crosspointStates = std::map<std::uint16_t, std::map<std::uint16_t, bool>>();
505 auto faderState = m_crosspointGainSliders.at(idx)->getToggleState();
506 auto in = std::uint16_t(i + 1);
507 auto out = std::uint16_t(o + 1);
508 crosspointStates[in][out] = faderState;
510 onCrosspointStatesChanged(crosspointStates);
511 addCrosspointStates(crosspointStates);
512 };
513 m_hvScrollContainerComponent->addAndMakeVisible(m_crosspointGainSliders.at(idx).get());
514 m_crosspointsControlsGrid->items.add(juce::GridItem(m_crosspointGainSliders.at(idx).get()));
515
516 }
517 }
518 }
519 for (auto i = 0; i < ioCount.first; i++)
520 {
521 for (auto o = 0; o < ioCount.second; o++)
522 {
523 auto idx = i * ioCount.second + o;
524 if (m_crosspointGainSliders.size() > idx)
525 {
526 auto in = std::uint16_t(i + 1);
527 auto out = std::uint16_t(o + 1);
528 auto crosspointState = ((getCrosspointStates().count(in) != 0) != 0 && getCrosspointStates().at(in).count(out) != 0) ? getCrosspointStates().at(in).at(out) : false;
529 auto crosspointValue = ((getCrosspointValues().count(in) != 0) != 0 && getCrosspointValues().at(in).count(out) != 0) ? getCrosspointValues().at(in).at(out) : 0.0f;
530 m_crosspointGainSliders.at(idx)->setToggleState(crosspointState, juce::dontSendNotification);
531 //m_crosspointGainSliders.at(idx)->setValue(juce::Decibels::gainToDecibels(double(crosspointState.second), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), juce::dontSendNotification);
532 m_crosspointGainSliders.at(idx)->setValue(double(crosspointValue), juce::dontSendNotification);
533 m_crosspointGainSliders.at(idx)->setVisible(true);
534 }
535 }
536 }
537 }
538}
539
540void FaderbankControlComponent::setInputMuteStates(const std::map<std::uint16_t, bool>& inputMuteStates)
541{
543
544 for (auto const& inputMuteStateKV : inputMuteStates)
545 {
546 auto& in = inputMuteStateKV.first;
547 auto i = in - 1;
548 auto& state = inputMuteStateKV.second;
549 if (m_inputMuteButtons.size() > i && nullptr != m_inputMuteButtons.at(i))
550 m_inputMuteButtons.at(i)->setToggleState(state, juce::dontSendNotification);
551 }
552}
553
554void FaderbankControlComponent::setOutputMuteStates(const std::map<std::uint16_t, bool>& outputMuteStates)
555{
557
558 for (auto const& outputMuteStateKV : outputMuteStates)
559 {
560 auto& out = outputMuteStateKV.first;
561 auto o = out - 1;
562 auto& state = outputMuteStateKV.second;
563 if (m_outputMuteButtons.size() > o && nullptr != m_outputMuteButtons.at(o))
564 m_outputMuteButtons.at(o)->setToggleState(state, juce::dontSendNotification);
565 }
566}
567
568void FaderbankControlComponent::setCrosspointStates(const std::map<std::uint16_t, std::map<std::uint16_t, bool>>& crosspointStates)
569{
571
573}
574
575void FaderbankControlComponent::setCrosspointValues(const std::map<std::uint16_t, std::map<std::uint16_t, float>>& crosspointValues)
576{
578
580}
581
583{
584 jassert((direction == ControlDirection::None && channel == 0) || (direction != ControlDirection::None && channel != 0));
585 auto oldDirection = m_currentIOChannel.first;
586 auto oldChannel = m_currentIOChannel.second;
587 m_currentIOChannel = std::make_pair(direction, channel);
588 if (oldDirection != direction)
589 rebuildControls(true);
590 else if (oldChannel != channel)
592
593 auto ioCount = getIOCount();
594 for (auto i = 0; i < ioCount.first; i++)
595 {
596 if (nullptr != m_inputSelectButtons.at(i))
597 {
598 auto in = std::uint16_t(i + 1);
599 auto state = (ControlDirection::Input == direction && channel == in);
600 m_inputSelectButtons.at(i)->setToggleState(state, juce::dontSendNotification);
601 }
602 }
603 for (auto o = 0; o < ioCount.second; o++)
604 {
605 if (nullptr != m_outputSelectButtons.at(o))
606 {
607 auto out = std::uint16_t(o + 1);
608 auto state = (ControlDirection::Output == direction && channel == out);
609 m_outputSelectButtons.at(o)->setToggleState(state, juce::dontSendNotification);
610 }
611 }
612}
613
615{
616 auto& crosspointStates = getCrosspointStates();
617 auto crosspointValues = getCrosspointValues();
618 for (auto const& crosspointStateIKV : crosspointStates)
619 {
620 auto& in = crosspointStateIKV.first;
621 for (auto const& crosspointStateIOKV : crosspointStateIKV.second)
622 {
623 auto& out = crosspointStateIOKV.first;
624 auto& state = crosspointStateIOKV.second;
625 auto& value = crosspointValues[in][out];
626
627 auto i = in - 1;
628 auto o = out - 1;
629 auto idx = i * getIOCount().second + o;
630
631 if (ControlDirection::Input == m_currentIOChannel.first && in == m_currentIOChannel.second && m_crosspointGainSliders.size() > o)
632 {
633 m_crosspointGainSliders.at(o)->setToggleState(state, juce::dontSendNotification);
634 m_crosspointGainSliders.at(o)->setValue(value, juce::dontSendNotification);
635 }
636 else if (ControlDirection::Output == m_currentIOChannel.first && out == m_currentIOChannel.second && m_crosspointGainSliders.size() > i)
637 {
638 m_crosspointGainSliders.at(i)->setToggleState(state, juce::dontSendNotification);
639 m_crosspointGainSliders.at(i)->setValue(value, juce::dontSendNotification);
640 }
641 else if (ControlDirection::None == m_currentIOChannel.first && m_crosspointGainSliders.size() > idx)
642 {
643 m_crosspointGainSliders.at(idx)->setToggleState(state, juce::dontSendNotification);
644 m_crosspointGainSliders.at(idx)->setValue(value, juce::dontSendNotification);
645 }
646 }
647 }
648}
649
650
651} // namespace Mema
void setIOCount(const std::pair< int, int > &ioCount) override
void setInputMuteStates(const std::map< std::uint16_t, bool > &inputMuteStates) override
void selectIOChannel(const ControlDirection &direction, int channel)
void setOutputMuteStates(const std::map< std::uint16_t, bool > &outputMuteStates) override
void setCrosspointValues(const std::map< std::uint16_t, std::map< std::uint16_t, float > > &crosspointValues) override
void setControlsSize(const ControlsSize &ctrlsSize) override
void setCrosspointStates(const std::map< std::uint16_t, std::map< std::uint16_t, bool > > &crosspointStates) override
Abstract base for all Mema.Re client control panels.
const std::map< std::uint16_t, bool > & getInputMuteStates()
virtual void addCrosspointValues(const std::map< std::uint16_t, std::map< std::uint16_t, float > > &crosspointValues)
ControlDirection
Direction of a control strip within the panel layout.
const std::map< std::uint16_t, bool > & getOutputMuteStates()
std::function< void(const std::map< std::uint16_t, bool > &)> onInputMutesChanged
virtual void setControlsSize(const ControlsSize &ctrlsSize)
std::function< void(const std::map< std::uint16_t, bool > &)> onOutputMutesChanged
std::function< void(const std::map< std::uint16_t, std::map< std::uint16_t, float > > &)> onCrosspointValuesChanged
const std::map< std::uint16_t, std::map< std::uint16_t, bool > > & getCrosspointStates()
virtual void addCrosspointStates(const std::map< std::uint16_t, std::map< std::uint16_t, bool > > &crosspointStates)
virtual void setIOCount(const std::pair< int, int > &ioCount)
virtual void setCrosspointStates(const std::map< std::uint16_t, std::map< std::uint16_t, bool > > &crosspointStates)
virtual void setCrosspointValues(const std::map< std::uint16_t, std::map< std::uint16_t, float > > &crosspointValues)
virtual void setInputMuteStates(const std::map< std::uint16_t, bool > &inputMuteStates)
const std::map< std::uint16_t, std::map< std::uint16_t, float > > & getCrosspointValues()
virtual void setOutputMuteStates(const std::map< std::uint16_t, bool > &outputMuteStates)
std::function< void(const std::map< std::uint16_t, std::map< std::uint16_t, bool > > &)> onCrosspointStatesChanged
ControlsSize
Size category for rendered control elements.
Definition Mema.cpp:27