Mema
Memory Matrix — multi-channel audio matrix monitor and router
Loading...
Searching...
No Matches
MemaReComponent.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
19#include "MemaReComponent.h"
20
23
25 : juce::Component()
26{
27 m_faderbankCtrlComponent = std::make_unique<Mema::FaderbankControlComponent>();
28 m_faderbankCtrlComponent->onInputMutesChanged = [=](const std::map<std::uint16_t, bool>& inputMuteStates) {
29 std::map<std::uint16_t, bool> outputMuteStates;
30 std::map<std::uint16_t, std::map<std::uint16_t, bool>> crosspointStates;
31 std::map<std::uint16_t, std::map<std::uint16_t, float>> crosspointValues;
33 onMessageReadyToSend(std::make_unique<Mema::ControlParametersMessage>(inputMuteStates, outputMuteStates, crosspointStates, crosspointValues)->getSerializedMessage());
34 };
35 m_faderbankCtrlComponent->onOutputMutesChanged = [=](const std::map<std::uint16_t, bool>& outputMuteStates) {
36 std::map<std::uint16_t, bool> inputMuteStates;
37 std::map<std::uint16_t, std::map<std::uint16_t, bool>> crosspointStates;
38 std::map<std::uint16_t, std::map<std::uint16_t, float>> crosspointValues;
40 onMessageReadyToSend(std::make_unique<Mema::ControlParametersMessage>(inputMuteStates, outputMuteStates, crosspointStates, crosspointValues)->getSerializedMessage());
41 };
42 m_faderbankCtrlComponent->onCrosspointStatesChanged = [=](const std::map<std::uint16_t, std::map<std::uint16_t, bool>>& crosspointStates) {
43 std::map<std::uint16_t, bool> inputMuteStates;
44 std::map<std::uint16_t, bool> outputMuteStates;
45 std::map<std::uint16_t, std::map<std::uint16_t, float>> crosspointValues;
47 onMessageReadyToSend(std::make_unique<Mema::ControlParametersMessage>(inputMuteStates, outputMuteStates, crosspointStates, crosspointValues)->getSerializedMessage());
48 };
49 m_faderbankCtrlComponent->onCrosspointValuesChanged = [=](const std::map<std::uint16_t, std::map<std::uint16_t, float>>& crosspointValues) {
50 std::map<std::uint16_t, bool> inputMuteStates;
51 std::map<std::uint16_t, bool> outputMuteStates;
52 std::map<std::uint16_t, std::map<std::uint16_t, bool>> crosspointStates;
54 onMessageReadyToSend(std::make_unique<Mema::ControlParametersMessage>(inputMuteStates, outputMuteStates, crosspointStates, crosspointValues)->getSerializedMessage());
55 };
56 addChildComponent(m_faderbankCtrlComponent.get());
57
58 m_panningCtrlComponent = std::make_unique<Mema::PanningControlComponent>();
59 m_panningCtrlComponent->onInputMutesChanged = [=](const std::map<std::uint16_t, bool>& inputMuteStates) {
60 std::map<std::uint16_t, bool> outputMuteStates;
61 std::map<std::uint16_t, std::map<std::uint16_t, bool>> crosspointStates;
62 std::map<std::uint16_t, std::map<std::uint16_t, float>> crosspointValues;
64 onMessageReadyToSend(std::make_unique<Mema::ControlParametersMessage>(inputMuteStates, outputMuteStates, crosspointStates, crosspointValues)->getSerializedMessage());
65 };
66 m_panningCtrlComponent->onOutputMutesChanged = [=](const std::map<std::uint16_t, bool>& outputMuteStates) {
67 std::map<std::uint16_t, bool> inputMuteStates;
68 std::map<std::uint16_t, std::map<std::uint16_t, bool>> crosspointStates;
69 std::map<std::uint16_t, std::map<std::uint16_t, float>> crosspointValues;
71 onMessageReadyToSend(std::make_unique<Mema::ControlParametersMessage>(inputMuteStates, outputMuteStates, crosspointStates, crosspointValues)->getSerializedMessage());
72 };
73 m_panningCtrlComponent->onCrosspointStatesChanged = [=](const std::map<std::uint16_t, std::map<std::uint16_t, bool>>& crosspointStates) {
74 std::map<std::uint16_t, bool> inputMuteStates;
75 std::map<std::uint16_t, bool> outputMuteStates;
76 std::map<std::uint16_t, std::map<std::uint16_t, float>> crosspointValues;
78 onMessageReadyToSend(std::make_unique<Mema::ControlParametersMessage>(inputMuteStates, outputMuteStates, crosspointStates, crosspointValues)->getSerializedMessage());
79 };
80 m_panningCtrlComponent->onCrosspointValuesChanged = [=](const std::map<std::uint16_t, std::map<std::uint16_t, float>>& crosspointValues) {
81 std::map<std::uint16_t, bool> inputMuteStates;
82 std::map<std::uint16_t, bool> outputMuteStates;
83 std::map<std::uint16_t, std::map<std::uint16_t, bool>> crosspointStates;
85 onMessageReadyToSend(std::make_unique<Mema::ControlParametersMessage>(inputMuteStates, outputMuteStates, crosspointStates, crosspointValues)->getSerializedMessage());
86 };
87 m_panningCtrlComponent->setExternalControlSettings(std::get<0>(m_externalAdmOscSettings), std::get<1>(m_externalAdmOscSettings), std::get<2>(m_externalAdmOscSettings));
88 addChildComponent(m_panningCtrlComponent.get());
89
90 m_pluginCtrlComponent = std::make_unique<Mema::PluginControlComponent>();
91 m_pluginCtrlComponent->onPluginParameterValueChanged = [=](std::uint16_t parameterIndex, std::string parameterId, float value) {
92 DBG(juce::String(__FUNCTION__) + " sending to net (" + juce::String(parameterIndex) + "; " + juce::String(parameterId) + "; " + juce::String(value) + ") ...");
94 onMessageReadyToSend(std::make_unique<Mema::PluginParameterValueMessage>(parameterIndex, parameterId, value)->getSerializedMessage());
95 };
96 m_pluginCtrlComponent->onPluginEnabledChanged = [=](bool enabled) {
97 m_pluginEnabled = enabled;
98 DBG(juce::String(__FUNCTION__) + " sending enabled state to net: " + juce::String(int(enabled)));
100 onMessageReadyToSend(std::make_unique<Mema::PluginProcessingStateMessage>(m_pluginEnabled, m_pluginPost)->getSerializedMessage());
101 };
102 m_pluginCtrlComponent->onPluginPrePostChanged = [=](bool post) {
103 m_pluginPost = post;
104 DBG(juce::String(__FUNCTION__) + " sending pre/post state to net: " + juce::String(int(post)));
106 onMessageReadyToSend(std::make_unique<Mema::PluginProcessingStateMessage>(m_pluginEnabled, m_pluginPost)->getSerializedMessage());
107 };
108 addChildComponent(m_pluginCtrlComponent.get());
109
111}
112
116
118{
119 auto resizeRequired = false;
120
121 if (m_faderbankCtrlComponent)
122 {
123 if (!m_faderbankCtrlComponent->isVisible())
124 {
125 m_faderbankCtrlComponent->setIOCount(m_currentIOCount);
126 m_faderbankCtrlComponent->setVisible(true);
127 resizeRequired = true;
128 }
129 }
130 if (m_panningCtrlComponent && m_panningCtrlComponent->isVisible())
131 {
132 m_panningCtrlComponent->resetCtrl();
133 m_panningCtrlComponent->setVisible(false);
134 resizeRequired = true;
135 }
136 if (m_pluginCtrlComponent && m_pluginCtrlComponent->isVisible())
137 {
138 m_pluginCtrlComponent->resetCtrl();
139 m_pluginCtrlComponent->setVisible(false);
140 resizeRequired = true;
141 }
142
143 if (resizeRequired && !getLocalBounds().isEmpty())
144 resized();
145}
146
147void MemaReComponent::setOutputPanningCtrlActive(const juce::AudioChannelSet& channelConfiguration)
148{
149 auto resizeRequired = false;
150
151 if (m_panningCtrlComponent)
152 {
153 if (!m_panningCtrlComponent->isVisible())
154 {
155 m_panningCtrlComponent->setIOCount(m_currentIOCount);
156 m_panningCtrlComponent->setVisible(true);
157 if (!m_inputMuteStates.empty())
158 m_panningCtrlComponent->setInputMuteStates(m_inputMuteStates);
159 if (!m_outputMuteStates.empty())
160 m_panningCtrlComponent->setOutputMuteStates(m_outputMuteStates);
161 if (!m_crosspointStates.empty())
162 m_panningCtrlComponent->setCrosspointStates(m_crosspointStates);
163 if (!m_crosspointValues.empty())
164 m_panningCtrlComponent->setCrosspointValues(m_crosspointValues);
165 resizeRequired = true;
166 }
167 m_panningCtrlComponent->setChannelConfig(channelConfiguration);
168 }
169 if (m_faderbankCtrlComponent && m_faderbankCtrlComponent->isVisible())
170 {
171 m_faderbankCtrlComponent->resetCtrl();
172 m_faderbankCtrlComponent->setVisible(false);
173 resizeRequired = true;
174 }
175 if (m_pluginCtrlComponent && m_pluginCtrlComponent->isVisible())
176 {
177 m_pluginCtrlComponent->resetCtrl();
178 m_pluginCtrlComponent->setVisible(false);
179 resizeRequired = true;
180 }
181
182 if (resizeRequired && !getLocalBounds().isEmpty())
183 resized();
184}
185
187{
188 auto resizeRequired = false;
189
190 if (m_pluginCtrlComponent)
191 {
192 if (!m_pluginCtrlComponent->isVisible())
193 {
194 m_pluginCtrlComponent->setVisible(true);
195 resizeRequired = true;
196 }
197 // Apply any data that arrived before the component was made visible
198 if (!m_pluginName.empty() || !m_pluginParameterInfos.empty())
199 {
200 m_pluginCtrlComponent->setPluginName(m_pluginName);
201 m_pluginCtrlComponent->setParameterInfos(m_pluginParameterInfos);
202 }
203 m_pluginCtrlComponent->setPluginEnabled(m_pluginEnabled);
204 m_pluginCtrlComponent->setPluginPrePost(m_pluginPost);
205 }
206 if (m_faderbankCtrlComponent && m_faderbankCtrlComponent->isVisible())
207 {
208 m_faderbankCtrlComponent->resetCtrl();
209 m_faderbankCtrlComponent->setVisible(false);
210 resizeRequired = true;
211 }
212 if (m_panningCtrlComponent && m_panningCtrlComponent->isVisible())
213 {
214 m_panningCtrlComponent->resetCtrl();
215 m_panningCtrlComponent->setVisible(false);
216 resizeRequired = true;
217 }
218
219 if (resizeRequired && !getLocalBounds().isEmpty())
220 resized();
221}
222
224{
225 m_pluginName.clear();
226 m_pluginParameterInfos.clear();
227 m_pluginEnabled = false;
228 m_pluginPost = false;
229 m_inputMuteStates.clear();
230 m_outputMuteStates.clear();
231 m_crosspointStates.clear();
232 m_crosspointValues.clear();
233
234 if (m_faderbankCtrlComponent)
235 m_faderbankCtrlComponent->resetCtrl();
236 if (m_panningCtrlComponent)
237 m_panningCtrlComponent->resetCtrl();
238 if (m_pluginCtrlComponent)
239 m_pluginCtrlComponent->resetCtrl();
240}
241
243{
244 if (m_faderbankCtrlComponent)
245 m_faderbankCtrlComponent->setControlsSize(ctrlsSize);
246 if (m_panningCtrlComponent)
247 m_panningCtrlComponent->setControlsSize(ctrlsSize);
248 if (m_pluginCtrlComponent)
249 m_pluginCtrlComponent->setControlsSize(ctrlsSize);
250}
251
253{
254 if (m_faderbankCtrlComponent)
255 return m_faderbankCtrlComponent->getControlsSize();
256 else if (m_panningCtrlComponent)
257 return m_panningCtrlComponent->getControlsSize();
258 else if (m_pluginCtrlComponent)
259 return m_pluginCtrlComponent->getControlsSize();
260 else
262}
263
264void MemaReComponent::setExternalAdmOscSettings(const int ADMOSCport, const juce::IPAddress& ADMOSCremoteIP, const int ADMOSCremotePort)
265{
266 std::get<0>(m_externalAdmOscSettings) = ADMOSCport;
267 std::get<1>(m_externalAdmOscSettings) = ADMOSCremoteIP;
268 std::get<2>(m_externalAdmOscSettings) = ADMOSCremotePort;
269
270 m_panningCtrlComponent->setExternalControlSettings(ADMOSCport, ADMOSCremoteIP, ADMOSCremotePort);
271}
272
273std::tuple<int, juce::IPAddress, int> MemaReComponent::getExternalAdmOscSettings()
274{
275 return m_externalAdmOscSettings;
276}
277
278void MemaReComponent::paint(Graphics &g)
279{
280 g.fillAll(getLookAndFeel().findColour(juce::LookAndFeel_V4::ColourScheme::widgetBackground));
281}
282
284{
285 if (m_faderbankCtrlComponent && m_faderbankCtrlComponent->isVisible())
286 m_faderbankCtrlComponent->setBounds(getLocalBounds());
287 if (m_panningCtrlComponent && m_panningCtrlComponent->isVisible())
288 m_panningCtrlComponent->setBounds(getLocalBounds());
289 if (m_pluginCtrlComponent && m_pluginCtrlComponent->isVisible())
290 m_pluginCtrlComponent->setBounds(getLocalBounds());
291}
292
293void MemaReComponent::handleMessage(const Message& message)
294{
295 if (RunningStatus::Active != m_runningStatus)
296 {
297 m_runningStatus = RunningStatus::Active;
298 resized();
299 }
300
301 if (auto const apm = dynamic_cast<const Mema::AnalyzerParametersMessage*>(&message))
302 {
303 // we don't want analyzer parameter infos!
304 DBG(juce::String(__FUNCTION__) + " ignoring unexpected AnalyzerParametersMessage...");
305 }
306 else if (auto m = dynamic_cast<const Mema::AudioBufferMessage*>(&message))
307 {
308 // we don't want audio buffer infos!
309 DBG(juce::String(__FUNCTION__) + " ignoring unexpected AudiBufferMessage...");
310 }
311 else if (auto const iom = dynamic_cast<const Mema::ReinitIOCountMessage*>(&message))
312 {
313 DBG(juce::String(__FUNCTION__) + " handling ReinitIOCountMessage...");
314
315 auto inputCount = iom->getInputCount();
316 jassert(inputCount > 0);
317 auto outputCount = iom->getOutputCount();
318 jassert(outputCount > 0);
319
320 m_currentIOCount = std::make_pair(inputCount, outputCount);
321
322 if (m_faderbankCtrlComponent)
323 m_faderbankCtrlComponent->setIOCount(m_currentIOCount);
324 if (m_panningCtrlComponent && m_panningCtrlComponent->isVisible())
325 m_panningCtrlComponent->setIOCount(m_currentIOCount);
326
327 resized();
328 }
329 else if (auto const cpm = dynamic_cast<const Mema::ControlParametersMessage*>(&message))
330 {
331 DBG(juce::String(__FUNCTION__) + " handling ControlParametersMessage...");
332
333 for (auto const& inputMuteState : cpm->getInputMuteStates())
334 m_inputMuteStates[inputMuteState.first] = inputMuteState.second;
335 if (!m_inputMuteStates.empty())
336 {
337 if (m_faderbankCtrlComponent)
338 m_faderbankCtrlComponent->setInputMuteStates(m_inputMuteStates);
339 if (m_panningCtrlComponent && m_panningCtrlComponent->isVisible())
340 m_panningCtrlComponent->setInputMuteStates(m_inputMuteStates);
341 }
342
343 for (auto const& outputMuteState : cpm->getOutputMuteStates())
344 m_outputMuteStates[outputMuteState.first] = outputMuteState.second;
345 if (!m_outputMuteStates.empty())
346 {
347 if (m_faderbankCtrlComponent)
348 m_faderbankCtrlComponent->setOutputMuteStates(m_outputMuteStates);
349 if (m_panningCtrlComponent && m_panningCtrlComponent->isVisible())
350 m_panningCtrlComponent->setOutputMuteStates(m_outputMuteStates);
351 }
352
353 for (auto const& cpsIKV : cpm->getCrosspointStates())
354 for (auto const& cpsOKV : cpsIKV.second)
355 m_crosspointStates[cpsIKV.first][cpsOKV.first] = cpsOKV.second;
356 if (!m_crosspointStates.empty())
357 {
358 if (m_faderbankCtrlComponent)
359 m_faderbankCtrlComponent->setCrosspointStates(m_crosspointStates);
360 if (m_panningCtrlComponent && m_panningCtrlComponent->isVisible())
361 m_panningCtrlComponent->setCrosspointStates(m_crosspointStates);
362 }
363
364 for (auto const& cpvIKV : cpm->getCrosspointValues())
365 for (auto const& cpvOKV : cpvIKV.second)
366 m_crosspointValues[cpvIKV.first][cpvOKV.first] = cpvOKV.second;
367 if (!m_crosspointValues.empty())
368 {
369 if (m_faderbankCtrlComponent)
370 m_faderbankCtrlComponent->setCrosspointValues(m_crosspointValues);
371 if (m_panningCtrlComponent && m_panningCtrlComponent->isVisible())
372 m_panningCtrlComponent->setCrosspointValues(m_crosspointValues);
373 }
374
375 resized();
376 }
377 else if (auto const ppim = dynamic_cast<const Mema::PluginParameterInfosMessage*>(&message))
378 {
379 DBG(juce::String(__FUNCTION__) + " handling PluginParameterInfosMessage (" + juce::String(ppim->getParameterInfos().size()) + ") ...");
380
381 m_pluginName = ppim->getPluginName().toStdString();
382 m_pluginParameterInfos = ppim->getParameterInfos();
383 m_pluginEnabled = ppim->isPluginEnabled();
384 m_pluginPost = ppim->isPluginPost();
385
386 if (m_pluginCtrlComponent && m_pluginCtrlComponent->isVisible())
387 {
388 m_pluginCtrlComponent->setPluginName(m_pluginName);
389 m_pluginCtrlComponent->setParameterInfos(m_pluginParameterInfos);
390 m_pluginCtrlComponent->setPluginEnabled(m_pluginEnabled);
391 m_pluginCtrlComponent->setPluginPrePost(m_pluginPost);
392 }
393
394 resized();
395 }
396 else if (auto const pesm = dynamic_cast<const Mema::PluginProcessingStateMessage*>(&message))
397 {
398 DBG(juce::String(__FUNCTION__) + " handling PluginProcessingStateMessage (enabled:" + juce::String(int(pesm->isEnabled())) + " post:" + juce::String(int(pesm->isPost())) + ") ...");
399
400 m_pluginEnabled = pesm->isEnabled();
401 m_pluginPost = pesm->isPost();
402
403 if (m_pluginCtrlComponent && m_pluginCtrlComponent->isVisible())
404 {
405 m_pluginCtrlComponent->setPluginEnabled(m_pluginEnabled);
406 m_pluginCtrlComponent->setPluginPrePost(m_pluginPost);
407 }
408 }
409 else if (auto const ppvm = dynamic_cast<const Mema::PluginParameterValueMessage*>(&message))
410 {
411 DBG(juce::String(__FUNCTION__) + " handling PluginParameterValueMessage (" + juce::String(ppvm->getParameterIndex()) + "; " + ppvm->getParameterId() + "; " + juce::String(ppvm->getCurrentValue()) + ") ...");
412
413 auto paramIdx = ppvm->getParameterIndex();
414 if (paramIdx < m_pluginParameterInfos.size())
415 {
416 m_pluginParameterInfos[paramIdx].currentValue = ppvm->getCurrentValue();
417 m_pluginParameterInfos[paramIdx].id = ppvm->getParameterId();
418 }
419
420 if (m_pluginCtrlComponent && m_pluginCtrlComponent->isVisible())
421 m_pluginCtrlComponent->setParameterValue(paramIdx, ppvm->getParameterId().toStdString(), ppvm->getCurrentValue());
422
423 resized();
424 }
425}
426
~MemaReComponent() override
void resized() override
Lays out the active control component to fill the available area.
const Mema::FaderbankControlComponent::ControlsSize getControlsSize()
Returns the current control-element size setting.
void resetCtrl()
Resets all control components to their default/empty state.
@ Active
Actively receiving and sending control data to/from Mema.
void setOutputPanningCtrlActive(const juce::AudioChannelSet &channelConfiguration)
Switches to the 2-D spatial panning control for the given speaker layout.
void handleMessage(const Message &message) override
Dispatches inbound network messages to the appropriate control component.
void paint(juce::Graphics &g) override
Paints the background when no control mode is active.
void setFaderbankCtrlActive()
Switches to the faderbank (crosspoint slider/mute matrix) control mode.
void setPluginCtrlActive()
Switches to the plugin-parameter control mode.
void setControlsSize(const Mema::FaderbankControlComponent::ControlsSize &ctrlsSize)
Propagates a control-element size change (S/M/L) to the faderbank component.
void setExternalAdmOscSettings(const int ADMOSCport, const juce::IPAddress &ADMOSCremoteIP, const int ADMOSCremotePort)
Configures the ADM-OSC listener port and the remote-controller address used by the panning component.
std::function< void(const juce::MemoryBlock &)> onMessageReadyToSend
Invoked with a serialised message whenever a control value changes that must be sent to Mema.
std::tuple< int, juce::IPAddress, int > getExternalAdmOscSettings()
Returns the current ADM-OSC settings as {listenPort, remoteIP, remotePort}.
Carries audio-device parameters (sample rate, block size) from Mema to clients.
Base message carrying a serialised audio buffer and its flow-direction metadata.
Full routing-matrix state snapshot exchanged bidirectionally between Mema and Mema....
ControlsSize
Size category for rendered control elements.
@ S
Small — suited for desktop with many channels.
Carries the plugin name and complete parameter descriptor list from Mema to Mema.Re clients.
Carries a single normalised plugin parameter value from Mema.Re to Mema.
Carries the plugin enabled and pre/post insertion state between Mema and Mema.Re.
Instructs clients to tear down and rebuild their UI for a new channel count.