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
194
195void FaderbankControlComponent::setIOCount(const std::pair<int, int>& ioCount)
196{
198
200 selectIOChannel(m_currentIOChannel.first, m_currentIOChannel.second);
201}
202
210
212{
213 auto ioCount = getIOCount();
214
215 if (ioCount.first != m_inputSelectButtons.size() || ioCount.first != m_inputMuteButtons.size() || force)
216 {
217 auto templateColums = juce::Array<juce::Grid::TrackInfo>();
218 for (auto in = 0; in < ioCount.first; in++)
219 templateColums.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
220
221 m_inputMuteButtons.resize(ioCount.first);
222 m_inputSelectButtons.resize(ioCount.first);
223 m_inputControlsGrid->items.clear();
224 m_inputControlsGrid->templateRows = { juce::Grid::TrackInfo(juce::Grid::Fr(1)), juce::Grid::TrackInfo(juce::Grid::Fr(1)) };
225 m_inputControlsGrid->templateColumns = templateColums;
226
227 for (auto i = 0; i < ioCount.first; i++)
228 {
229 auto in = std::uint16_t(i + 1);
230 m_inputSelectButtons.at(i) = std::make_unique<juce::TextButton>("In " + juce::String(in));
231 m_inputSelectButtons.at(i)->setClickingTogglesState(true);
232 m_inputSelectButtons.at(i)->setColour(juce::TextButton::ColourIds::buttonOnColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
233 m_inputSelectButtons.at(i)->onClick = [this, i] {
234 if (m_inputSelectButtons.size() > i)
235 {
236 auto in = std::uint16_t(i + 1);
237 if (m_inputSelectButtons.at(i)->getToggleState())
239 else
241 }
242 else
243 jassertfalse;
244 };
245 if (ControlDirection::None == m_currentIOChannel.first)
246 m_hvScrollContainerComponent->addAndMakeVisible(m_inputSelectButtons.at(i).get());
247 else
248 m_horizontalScrollContainerComponent->addAndMakeVisible(m_inputSelectButtons.at(i).get());
249 m_inputControlsGrid->items.add(juce::GridItem(m_inputSelectButtons.at(i).get()));
250 }
251 for (auto i = 0; i < ioCount.first; i++)
252 {
253 auto in = std::uint16_t(i + 1);
254 m_inputMuteButtons.at(i) = std::make_unique<juce::TextButton>("M");
255 m_inputMuteButtons.at(i)->setClickingTogglesState(true);
256 m_inputMuteButtons.at(i)->setToggleState((getInputMuteStates().count(in) != 0 ? getInputMuteStates().at(in) : false), juce::dontSendNotification);
257 m_inputMuteButtons.at(i)->setColour(juce::TextButton::ColourIds::buttonOnColourId, juce::Colours::red);
258 m_inputMuteButtons.at(i)->onClick = [this, i] {
259 auto inputMuteStates = std::map<std::uint16_t, bool>();
260 auto in = std::uint16_t(i + 1);
261 inputMuteStates[in] = m_inputMuteButtons.at(i)->getToggleState();
264 onInputMutesChanged(inputMuteStates);
265 };
266 if (ControlDirection::None == m_currentIOChannel.first)
267 m_hvScrollContainerComponent->addAndMakeVisible(m_inputMuteButtons.at(i).get());
268 else
269 m_horizontalScrollContainerComponent->addAndMakeVisible(m_inputMuteButtons.at(i).get());
270 m_inputControlsGrid->items.add(juce::GridItem(m_inputMuteButtons.at(i).get()));
271 }
272 }
273}
274
276{
277 auto ioCount = getIOCount();
278
279 if (ioCount.second != m_outputSelectButtons.size() || ioCount.second != m_outputMuteButtons.size() || force)
280 {
281 auto templateRows = juce::Array<juce::Grid::TrackInfo>();
282 for (auto out = 0; out < ioCount.second; out++)
283 templateRows.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
284
285 m_outputMuteButtons.resize(ioCount.second);
286 m_outputSelectButtons.resize(ioCount.second);
287 m_outputControlsGrid->items.clear();
288 m_outputControlsGrid->templateRows = templateRows;
289 m_outputControlsGrid->templateColumns = { juce::Grid::TrackInfo(juce::Grid::Fr(1)), juce::Grid::TrackInfo(juce::Grid::Fr(1)) };
290
291 for (auto o = 0; o < ioCount.second; o++)
292 {
293 auto out = std::uint16_t(o + 1);
294 m_outputSelectButtons.at(o) = std::make_unique<juce::TextButton>("Out " + juce::String(out));
295 m_outputSelectButtons.at(o)->setClickingTogglesState(true);
296 m_outputSelectButtons.at(o)->setColour(juce::TextButton::ColourIds::buttonOnColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
297 m_outputSelectButtons.at(o)->onClick = [this, o] {
298 if (m_outputSelectButtons.size() > o)
299 {
300 auto out = std::uint16_t(o + 1);
301 if (m_outputSelectButtons.at(o)->getToggleState())
303 else
305 }
306 else
307 jassertfalse;
308 };
309 if (ControlDirection::None == m_currentIOChannel.first)
310 m_hvScrollContainerComponent->addAndMakeVisible(m_outputSelectButtons.at(o).get());
311 else
312 m_verticalScrollContainerComponent->addAndMakeVisible(m_outputSelectButtons.at(o).get());
313 m_outputControlsGrid->items.add(juce::GridItem(m_outputSelectButtons.at(o).get()));
314
315 m_outputMuteButtons.at(o) = std::make_unique<juce::TextButton>("M");
316 m_outputMuteButtons.at(o)->setClickingTogglesState(true);
317 m_outputMuteButtons.at(o)->setToggleState((getOutputMuteStates().count(out) != 0 ? getOutputMuteStates().at(out) : false), juce::dontSendNotification);
318 m_outputMuteButtons.at(o)->setColour(juce::TextButton::ColourIds::buttonOnColourId, juce::Colours::red);
319 m_outputMuteButtons.at(o)->onClick = [this, o] {
320 auto outputMuteStates = std::map<std::uint16_t, bool>();
321 auto out = std::uint16_t(o + 1);
322 outputMuteStates[out] = m_outputMuteButtons.at(o)->getToggleState();
325 onOutputMutesChanged(outputMuteStates);
326 };
327 if (ControlDirection::None == m_currentIOChannel.first)
328 m_hvScrollContainerComponent->addAndMakeVisible(m_outputMuteButtons.at(o).get());
329 else
330 m_verticalScrollContainerComponent->addAndMakeVisible(m_outputMuteButtons.at(o).get());
331 m_outputControlsGrid->items.add(juce::GridItem(m_outputMuteButtons.at(o).get()));
332 }
333 }
334}
335
337{
338 auto ioCount = getIOCount();
339
340 if (ControlDirection::Output == m_currentIOChannel.first)
341 {
342 if (force || ioCount.first != m_crosspointGainSliders.size())
343 {
344 auto templateColums = juce::Array<juce::Grid::TrackInfo>();
345 for (auto in = 0; in < ioCount.first; in++)
346 templateColums.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
347
348 m_crosspointGainSliders.resize(ioCount.first);
349 m_crosspointsControlsGrid->items.clear();
350 m_crosspointsControlsGrid->templateRows = { juce::Grid::TrackInfo(juce::Grid::Fr(1)) };
351 m_crosspointsControlsGrid->templateColumns = templateColums;
352
353 for (auto i = 0; i < ioCount.first; i++)
354 {
355 m_crosspointGainSliders.at(i) = std::make_unique<JUCEAppBasics::ToggleStateSlider>(juce::Slider::LinearVertical, juce::Slider::NoTextBox);
356 m_crosspointGainSliders.at(i)->setColour(juce::Slider::ColourIds::trackColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
357 m_crosspointGainSliders.at(i)->setRange(0.0, 1.0, 0.01);
358 m_crosspointGainSliders.at(i)->setTitle(juce::String(i + 1));
359 m_crosspointGainSliders.at(i)->displayValueConverter = [](double val) { return juce::String(juce::Decibels::gainToDecibels(val, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), 1) + " dB"; };
360 //juce::Decibels::gainToDecibels(0.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
361 //juce::Decibels::gainToDecibels(1.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
362 //0.1);
363 m_crosspointGainSliders.at(i)->onValueChange = [this, i] {
364 auto crosspointValues = std::map<std::uint16_t, std::map<std::uint16_t, float>>();
365 //auto faderValue = juce::Decibels::decibelsToGain(m_crosspointGainSliders.at(i)->getValue(), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB()));
366 auto faderValue = m_crosspointGainSliders.at(i)->getValue();
367 auto in = std::uint16_t(i + 1);
368 auto out = std::uint16_t(m_currentIOChannel.second);
369 crosspointValues[in][out] = float(faderValue);
371 onCrosspointValuesChanged(crosspointValues);
372 addCrosspointValues(crosspointValues);
373 };
374 m_crosspointGainSliders.at(i)->onToggleStateChange = [this, i] {
375 auto crosspointStates = std::map<std::uint16_t, std::map<std::uint16_t, bool>>();
376 //auto faderValue = juce::Decibels::decibelsToGain(m_crosspointGainSliders.at(i)->getValue(), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB()));
377 auto faderState = m_crosspointGainSliders.at(i)->getToggleState();
378 auto in = std::uint16_t(i + 1);
379 auto out = std::uint16_t(m_currentIOChannel.second);
380 crosspointStates[in][out] = faderState;
382 onCrosspointStatesChanged(crosspointStates);
383 addCrosspointStates(crosspointStates);
384 };
385 m_horizontalScrollContainerComponent->addAndMakeVisible(m_crosspointGainSliders.at(i).get());
386 m_crosspointsControlsGrid->items.add(juce::GridItem(m_crosspointGainSliders.at(i).get()));
387 }
388 }
389 for (auto i = 0; i < ioCount.first; i++)
390 {
391 if (m_crosspointGainSliders.size() > i)
392 {
393 auto in = std::uint16_t(i + 1);
394 auto out = std::uint16_t(m_currentIOChannel.second);
395 auto crosspointState = (getCrosspointStates().count(in) != 0 && getCrosspointStates().at(in).count(out) != 0) ? getCrosspointStates().at(in).at(out) : false;
396 auto crosspointValue = (getCrosspointValues().count(in) != 0 && getCrosspointValues().at(in).count(out) != 0) ? getCrosspointValues().at(in).at(out) : 0.0f;
397 m_crosspointGainSliders.at(i)->setToggleState(crosspointState, juce::dontSendNotification);
398 //m_crosspointGainSliders.at(i)->setValue(juce::Decibels::gainToDecibels(double(crosspointState.second), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), juce::dontSendNotification);
399 m_crosspointGainSliders.at(i)->setValue(double(crosspointValue), juce::dontSendNotification);
400 m_crosspointGainSliders.at(i)->setVisible(true);
401 }
402 }
403 }
404 else if (ControlDirection::Input == m_currentIOChannel.first)
405 {
406 if (force || ioCount.second != m_crosspointGainSliders.size())
407 {
408 auto templateRows = juce::Array<juce::Grid::TrackInfo>();
409 for (auto out = 0; out < ioCount.second; out++)
410 templateRows.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
411
412 m_crosspointGainSliders.resize(ioCount.second);
413 m_crosspointsControlsGrid->items.clear();
414 m_crosspointsControlsGrid->templateColumns = { juce::Grid::TrackInfo(juce::Grid::Fr(1)) };
415 m_crosspointsControlsGrid->templateRows = templateRows;
416
417 for (auto o = 0; o < ioCount.second; o++)
418 {
419 m_crosspointGainSliders.at(o) = std::make_unique<JUCEAppBasics::ToggleStateSlider>(juce::Slider::LinearHorizontal, juce::Slider::NoTextBox);
420 m_crosspointGainSliders.at(o)->setColour(juce::Slider::ColourIds::trackColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
421 m_crosspointGainSliders.at(o)->setRange(0.0, 1.0, 0.01);
422 m_crosspointGainSliders.at(o)->setTitle(juce::String(o + 1));
423 m_crosspointGainSliders.at(o)->displayValueConverter = [](double val) { return juce::String(juce::Decibels::gainToDecibels(val, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), 1) + " dB"; };
424 //juce::Decibels::gainToDecibels(0.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
425 //juce::Decibels::gainToDecibels(1.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
426 //0.1);
427 m_crosspointGainSliders.at(o)->onValueChange = [this, o] {
428 auto crosspointValues = std::map<std::uint16_t, std::map<std::uint16_t, float>>();
429 //auto faderValue = juce::Decibels::decibelsToGain(m_crosspointGainSliders.at(o)->getValue(), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB()));
430 auto faderValue = m_crosspointGainSliders.at(o)->getValue();
431 auto in = std::uint16_t(m_currentIOChannel.second);
432 auto out = std::uint16_t(o + 1);
433 crosspointValues[in][out] = float(faderValue);
435 onCrosspointValuesChanged(crosspointValues);
436 addCrosspointValues(crosspointValues);
437 };
438 m_crosspointGainSliders.at(o)->onToggleStateChange = [this, o] {
439 auto crosspointStates = std::map<std::uint16_t, std::map<std::uint16_t, bool>>();
440 auto faderState = m_crosspointGainSliders.at(o)->getToggleState();
441 auto in = std::uint16_t(m_currentIOChannel.second);
442 auto out = std::uint16_t(o + 1);
443 crosspointStates[in][out] = faderState;
445 onCrosspointStatesChanged(crosspointStates);
446 addCrosspointStates(crosspointStates);
447 };
448 m_verticalScrollContainerComponent->addAndMakeVisible(m_crosspointGainSliders.at(o).get());
449 m_crosspointsControlsGrid->items.add(juce::GridItem(m_crosspointGainSliders.at(o).get()));
450
451 }
452 }
453 for (auto o = 0; o < ioCount.second; o++)
454 {
455 if (m_crosspointGainSliders.size() > o)
456 {
457 auto in = std::uint16_t(m_currentIOChannel.second);
458 auto out = std::uint16_t(o + 1);
459 auto crosspointState = ((getCrosspointStates().count(in) != 0) != 0 && getCrosspointStates().at(in).count(out) != 0) ? getCrosspointStates().at(in).at(out) : false;
460 auto crosspointValue = ((getCrosspointValues().count(in) != 0) != 0 && getCrosspointValues().at(in).count(out) != 0) ? getCrosspointValues().at(in).at(out) : 0.0f;
461 m_crosspointGainSliders.at(o)->setToggleState(crosspointState, juce::dontSendNotification);
462 //m_crosspointGainSliders.at(o)->setValue(juce::Decibels::gainToDecibels(double(crosspointState.second), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), juce::dontSendNotification);
463 m_crosspointGainSliders.at(o)->setValue(double(crosspointValue), juce::dontSendNotification);
464 m_crosspointGainSliders.at(o)->setVisible(true);
465 }
466 }
467 }
468 else if (ControlDirection::None == m_currentIOChannel.first)
469 {
470 if (force || (ioCount.first * ioCount.second) != m_crosspointGainSliders.size())
471 {
472 auto templateColumns = juce::Array<juce::Grid::TrackInfo>();
473 for (auto in = 0; in < ioCount.first; in++)
474 templateColumns.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
475 auto templateRows = juce::Array<juce::Grid::TrackInfo>();
476 for (auto out = 0; out < ioCount.second; out++)
477 templateRows.add(juce::Grid::TrackInfo(juce::Grid::Px(m_controlsSize)));
478
479 m_crosspointGainSliders.resize(ioCount.first * ioCount.second);
480 m_crosspointsControlsGrid->items.clear();
481 m_crosspointsControlsGrid->templateColumns = templateColumns;
482 m_crosspointsControlsGrid->templateRows = templateRows;
483
484 for (auto o = 0; o < ioCount.second; o++)
485 {
486 for (auto i = 0; i < ioCount.first; i++)
487 {
488 auto idx = o + i * ioCount.second;
489 m_crosspointGainSliders.at(idx) = std::make_unique<JUCEAppBasics::ToggleStateSlider>(juce::Slider::Rotary, juce::Slider::NoTextBox);
490 m_crosspointGainSliders.at(idx)->setColour(juce::Slider::ColourIds::trackColourId, getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::ColourIds::MeteringRmsColourId));
491 m_crosspointGainSliders.at(idx)->setRange(0.0, 1.0, 0.01);
492 m_crosspointGainSliders.at(idx)->displayValueConverter = [](double val) { return juce::String(juce::Decibels::gainToDecibels(val, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), 1) + " dB"; };
493 //juce::Decibels::gainToDecibels(0.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
494 //juce::Decibels::gainToDecibels(1.0, static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())),
495 //0.1);
496 m_crosspointGainSliders.at(idx)->onValueChange = [this, idx, i, o] {
497 auto crosspointValues = std::map<std::uint16_t, std::map<std::uint16_t, float>>();
498 //auto faderValue = juce::Decibels::decibelsToGain(m_crosspointGainSliders.at(o)->getValue(), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB()));
499 auto faderValue = m_crosspointGainSliders.at(idx)->getValue();
500 auto in = std::uint16_t(i + 1);
501 auto out = std::uint16_t(o + 1);
502 crosspointValues[in][out] = float(faderValue);
504 onCrosspointValuesChanged(crosspointValues);
505 addCrosspointValues(crosspointValues);
506 };
507 m_crosspointGainSliders.at(idx)->onToggleStateChange = [this, idx, i, o] {
508 auto crosspointStates = std::map<std::uint16_t, std::map<std::uint16_t, bool>>();
509 auto faderState = m_crosspointGainSliders.at(idx)->getToggleState();
510 auto in = std::uint16_t(i + 1);
511 auto out = std::uint16_t(o + 1);
512 crosspointStates[in][out] = faderState;
514 onCrosspointStatesChanged(crosspointStates);
515 addCrosspointStates(crosspointStates);
516 };
517 m_hvScrollContainerComponent->addAndMakeVisible(m_crosspointGainSliders.at(idx).get());
518 m_crosspointsControlsGrid->items.add(juce::GridItem(m_crosspointGainSliders.at(idx).get()));
519
520 }
521 }
522 }
523 for (auto i = 0; i < ioCount.first; i++)
524 {
525 for (auto o = 0; o < ioCount.second; o++)
526 {
527 auto idx = i * ioCount.second + o;
528 if (m_crosspointGainSliders.size() > idx)
529 {
530 auto in = std::uint16_t(i + 1);
531 auto out = std::uint16_t(o + 1);
532 auto crosspointState = ((getCrosspointStates().count(in) != 0) != 0 && getCrosspointStates().at(in).count(out) != 0) ? getCrosspointStates().at(in).at(out) : false;
533 auto crosspointValue = ((getCrosspointValues().count(in) != 0) != 0 && getCrosspointValues().at(in).count(out) != 0) ? getCrosspointValues().at(in).at(out) : 0.0f;
534 m_crosspointGainSliders.at(idx)->setToggleState(crosspointState, juce::dontSendNotification);
535 //m_crosspointGainSliders.at(idx)->setValue(juce::Decibels::gainToDecibels(double(crosspointState.second), static_cast<double>(ProcessorDataAnalyzer::getGlobalMindB())), juce::dontSendNotification);
536 m_crosspointGainSliders.at(idx)->setValue(double(crosspointValue), juce::dontSendNotification);
537 m_crosspointGainSliders.at(idx)->setVisible(true);
538 }
539 }
540 }
541 }
542}
543
544void FaderbankControlComponent::setInputMuteStates(const std::map<std::uint16_t, bool>& inputMuteStates)
545{
547
548 for (auto const& inputMuteStateKV : inputMuteStates)
549 {
550 auto& in = inputMuteStateKV.first;
551 auto i = in - 1;
552 auto& state = inputMuteStateKV.second;
553 if (m_inputMuteButtons.size() > i && nullptr != m_inputMuteButtons.at(i))
554 m_inputMuteButtons.at(i)->setToggleState(state, juce::dontSendNotification);
555 }
556}
557
558void FaderbankControlComponent::setOutputMuteStates(const std::map<std::uint16_t, bool>& outputMuteStates)
559{
561
562 for (auto const& outputMuteStateKV : outputMuteStates)
563 {
564 auto& out = outputMuteStateKV.first;
565 auto o = out - 1;
566 auto& state = outputMuteStateKV.second;
567 if (m_outputMuteButtons.size() > o && nullptr != m_outputMuteButtons.at(o))
568 m_outputMuteButtons.at(o)->setToggleState(state, juce::dontSendNotification);
569 }
570}
571
572void FaderbankControlComponent::setCrosspointStates(const std::map<std::uint16_t, std::map<std::uint16_t, bool>>& crosspointStates)
573{
575
577}
578
579void FaderbankControlComponent::setCrosspointValues(const std::map<std::uint16_t, std::map<std::uint16_t, float>>& crosspointValues)
580{
582
584}
585
587{
588 jassert((direction == ControlDirection::None && channel == 0) || (direction != ControlDirection::None && channel != 0));
589 auto oldDirection = m_currentIOChannel.first;
590 auto oldChannel = m_currentIOChannel.second;
591 m_currentIOChannel = std::make_pair(direction, channel);
592 if (oldDirection != direction)
593 rebuildControls(true);
594 else if (oldChannel != channel)
596
597 auto ioCount = getIOCount();
598 for (auto i = 0; i < ioCount.first; i++)
599 {
600 if (nullptr != m_inputSelectButtons.at(i))
601 {
602 auto in = std::uint16_t(i + 1);
603 auto state = (ControlDirection::Input == direction && channel == in);
604 m_inputSelectButtons.at(i)->setToggleState(state, juce::dontSendNotification);
605 }
606 }
607 for (auto o = 0; o < ioCount.second; o++)
608 {
609 if (nullptr != m_outputSelectButtons.at(o))
610 {
611 auto out = std::uint16_t(o + 1);
612 auto state = (ControlDirection::Output == direction && channel == out);
613 m_outputSelectButtons.at(o)->setToggleState(state, juce::dontSendNotification);
614 }
615 }
616}
617
619{
620 auto& crosspointStates = getCrosspointStates();
621 auto crosspointValues = getCrosspointValues();
622 for (auto const& crosspointStateIKV : crosspointStates)
623 {
624 auto& in = crosspointStateIKV.first;
625 for (auto const& crosspointStateIOKV : crosspointStateIKV.second)
626 {
627 auto& out = crosspointStateIOKV.first;
628 auto& state = crosspointStateIOKV.second;
629 auto& value = crosspointValues[in][out];
630
631 auto i = in - 1;
632 auto o = out - 1;
633 auto idx = i * getIOCount().second + o;
634
635 if (ControlDirection::Input == m_currentIOChannel.first && in == m_currentIOChannel.second && m_crosspointGainSliders.size() > o)
636 {
637 m_crosspointGainSliders.at(o)->setToggleState(state, juce::dontSendNotification);
638 m_crosspointGainSliders.at(o)->setValue(value, juce::dontSendNotification);
639 }
640 else if (ControlDirection::Output == m_currentIOChannel.first && out == m_currentIOChannel.second && m_crosspointGainSliders.size() > i)
641 {
642 m_crosspointGainSliders.at(i)->setToggleState(state, juce::dontSendNotification);
643 m_crosspointGainSliders.at(i)->setValue(value, juce::dontSendNotification);
644 }
645 else if (ControlDirection::None == m_currentIOChannel.first && m_crosspointGainSliders.size() > idx)
646 {
647 m_crosspointGainSliders.at(idx)->setToggleState(state, juce::dontSendNotification);
648 m_crosspointGainSliders.at(idx)->setValue(value, juce::dontSendNotification);
649 }
650 }
651 }
652}
653
654
655} // 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.