Mema
Memory Matrix — multi-channel audio matrix monitor and router
Loading...
Searching...
No Matches
MemaClientDiscoverComponent.cpp
Go to the documentation of this file.
1/* Copyright (c) 2024-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
22
23#include <CustomLookAndFeel.h>
24#include <ServiceTopologyTreeView.h>
25
26
28 : juce::Component()
29{
30 m_discoveredTopologyLabel = std::make_unique<juce::Label>("TopologyLabel", "Available Mema sessions:");
31 addAndMakeVisible(m_discoveredTopologyLabel.get());
32
33 m_discoveredTopologyTreeView = std::make_unique<JUCEAppBasics::ServiceTopologyTreeView>(true);
34 m_discoveredTopologyTreeView->setDefaultOpenness(true);
35 addAndMakeVisible(m_discoveredTopologyTreeView.get());
36
37 m_selectServiceButton = std::make_unique<juce::TextButton>("Join session", "Join the selected Mema session.");
38 m_selectServiceButton->onClick = [=]() {
39 if (onServiceSelected && m_discoveredTopologyTreeView)
40 {
41 auto item = dynamic_cast<JUCEAppBasics::MasterServiceTreeViewItem*>(m_discoveredTopologyTreeView->getSelectedItem(0));
42 if (nullptr != item)
43 onServiceSelected(item->getServiceInfo());
44 }
45 };
46 addAndMakeVisible(m_selectServiceButton.get());
47}
48
52
54{
55 g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::ColourIds::backgroundColourId));
56}
57
59{
60 auto labelHeight = 35;
61 auto buttonHeight = 35;
62 auto margin = 4;
63 auto maxDiscoveryElmsWidth = 450;
64 auto maxDiscoveryElmsHeight = 350;
65
66 auto bounds = getLocalBounds().reduced(2*margin);
67 if (bounds.getWidth() > maxDiscoveryElmsWidth)
68 {
69 auto hmargin = int((bounds.getWidth() - maxDiscoveryElmsWidth) * 0.5f);
70 bounds.removeFromLeft(hmargin);
71 bounds.removeFromRight(hmargin);
72 }
73 if (bounds.getHeight() > maxDiscoveryElmsHeight)
74 {
75 auto vmargin = int((bounds.getHeight() - maxDiscoveryElmsHeight) * 0.5f);
76 bounds.removeFromTop(vmargin);
77 bounds.removeFromBottom(vmargin);
78 }
79
80 if (m_selectServiceButton)
81 m_selectServiceButton->setBounds(bounds.removeFromBottom(buttonHeight));
82 bounds.removeFromBottom(margin);
83 if (m_discoveredTopologyLabel)
84 m_discoveredTopologyLabel->setBounds(bounds.removeFromTop(labelHeight));
85 if (m_discoveredTopologyTreeView)
86 m_discoveredTopologyTreeView->setBounds(bounds);
87}
88
90{
91 getLookAndFeel().setColour(
92 TreeView::ColourIds::selectedItemBackgroundColourId,
93 getLookAndFeel().findColour(JUCEAppBasics::CustomLookAndFeel::MeteringRmsColourId));
94}
95
100
101void MemaClientDiscoverComponent::setupServiceDiscovery(const juce::String& serviceTypeUIDBase, const juce::String& serviceTypeUID)
102{
103 // scope to autodestruct testSocket
104 {
105 auto testSocket = std::make_unique<juce::DatagramSocket>();
106 testSocket->setEnablePortReuse(true);
107 if (!testSocket->bindToPort(Mema::ServiceData::getBroadcastPort()))
108 {
109 auto conflictTitle = "Service discovery error";
110 auto conflictInfo = "Unable to discover Mema instances\nas the discovery broadcast port cannot be used.";
111 juce::AlertWindow::showOkCancelBox(juce::MessageBoxIconType::WarningIcon, conflictTitle, conflictInfo, "Retry", "Quit", nullptr, juce::ModalCallbackFunction::create([this, serviceTypeUIDBase, serviceTypeUID](int result) {
112 if (1 == result)
113 setupServiceDiscovery(serviceTypeUIDBase, serviceTypeUID);
114 else
115 juce::JUCEApplication::getInstance()->quit();
116 }));
117 }
118 }
119
120 m_serviceTopologyManager = std::make_unique<JUCEAppBasics::ServiceTopologyManager>(
121 serviceTypeUIDBase, serviceTypeUID,
122 JUCEAppBasics::ServiceTopologyManager::getServiceDescription(),
123 "",
126
127 m_serviceTopologyManager->onDiscoveredTopologyChanged = [=]() {
128 if (m_serviceTopologyManager)
129 setDiscoveredServiceTopology(m_serviceTopologyManager->getDiscoveredServiceTopology());
130 };
131}
132
133std::vector<JUCEAppBasics::SessionMasterAwareService> MemaClientDiscoverComponent::getAvailableServices()
134{
135 std::vector<JUCEAppBasics::SessionMasterAwareService> services;
136 services.reserve(m_serviceTopologyManager->getDiscoveredServiceTopology().size());
137 for (auto const& serviceKV : m_serviceTopologyManager->getDiscoveredServiceTopology())
138 services.push_back(serviceKV.first);
139 return services;
140}
141void MemaClientDiscoverComponent::setMasterServiceDescription(const juce::String& masterServiceDescription)
142{
143 if (m_serviceTopologyManager)
144 m_serviceTopologyManager->setSessionMasterServiceDescription(masterServiceDescription);
145}
146
147void MemaClientDiscoverComponent::setDiscoveredServiceTopology(const JUCEAppBasics::SessionServiceTopology& topology)
148{
149 if (m_discoveredTopologyTreeView)
150 m_discoveredTopologyTreeView->setServiceTopology(topology);
151}
152
std::function< void(const JUCEAppBasics::SessionMasterAwareService &)> onServiceSelected
void setMasterServiceDescription(const juce::String &masterServiceDescription)
void setupServiceDiscovery(const juce::String &serviceTypeUIDBase, const juce::String &serviceTypeUID)
std::vector< JUCEAppBasics::SessionMasterAwareService > getAvailableServices()
static int getConnectionPort()
Returns the TCP port used for client connections (55668).
static int getBroadcastPort()
Returns the UDP port used for multicast service announcements.