Mema
Memory Matrix — multi-channel audio matrix monitor and router
Loading...
Searching...
No Matches
ADMOSController.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 "ADMOSController.h"
20
21
22namespace Mema
23{
24
25
26std::mutex ADMOSController::ADMOSCParameterChangedMessage::m_typeMapMutex;
27std::map<int, std::vector<std::uint16_t>> ADMOSController::ADMOSCParameterChangedMessage::m_typeMap;
28
29
30//==============================================================================
32{
33 m_oscReceiver = std::make_unique<juce::OSCReceiver>("ADM-OSC communication thread");
34 m_oscReceiver->registerFormatErrorHandler([=](const char* /*data*/, int dataSize) { DBG(juce::String(__FUNCTION__) + " received " + juce::String(dataSize) + " bytes of unknown data"); });
35 m_oscReceiver->addListener(this);
36
37 m_oscSender = std::make_unique<juce::OSCSender>();
38}
39
43
44bool ADMOSController::startConnection(int receiverOscPort, juce::IPAddress targetIP, int targetPort)
45{
46 DBG(juce::String(__FUNCTION__) << " RP:" << receiverOscPort << " TIP:" << targetIP.toString() << " TP:" << targetPort);
47
48 auto success = true;
49 if (m_oscReceiver)
50 {
51 auto connected = m_oscReceiver->connect(receiverOscPort);
52 if (!connected)
53 juce::AlertWindow::showMessageBoxAsync(juce::MessageBoxIconType::WarningIcon, "Communication issue", "ADM-OSC port " + juce::String(receiverOscPort) + " could not be opened.", "Ok");
54 success = success && connected;
55 }
56 else
57 success = false;
58
59 if (m_oscSender)
60 {
61 auto connected = m_oscSender->connect(targetIP.toString(), targetPort);
62 if (!connected)
63 juce::AlertWindow::showMessageBoxAsync(juce::MessageBoxIconType::WarningIcon, "Communication issue", "ADM-OSC connection to given " + targetIP.toString() + ":" + juce::String(targetPort) + " could not be established.", "Ok");
64 success = success && connected;
65 }
66 else
67 success = false;
68
69 return success;
70}
71
73{
74 m_knownObjNums.resize(numObjects);
75 for (int i = 1; i <= numObjects; i++)
76 {
77 m_knownObjNums[i-1] = i;
78 for (std::uint16_t t = ADMOSCParameterType::X; t <= ADMOSCParameterType::Mute; t++)
79 m_objCache[i][t] = ADMOSCParameter(ADMOSCParameterType(t));
80 }
81}
82
83const std::vector<int> ADMOSController::getObjNumsFromObjIdent(const juce::String& objIdent)
84{
85 auto objNums = std::vector<int>();
86 if (!objIdent.containsAnyOf("*?{}[]"))
87 objNums.push_back(objIdent.getIntValue());
88 else
89 {
90 if (objIdent == "*")
91 {
92 objNums = m_knownObjNums;
93 }
94 else if (objIdent.startsWith("{") && objIdent.endsWith("}"))
95 {
96 auto numListStr = objIdent.substring(1, objIdent.length());
97 juce::StringArray nsa;
98 nsa.addTokens(numListStr, ",", "");
99 objNums.reserve(nsa.size());
100 for (auto const& ns : nsa)
101 objNums.push_back(ns.getIntValue());
102 }
103 else if (objIdent.startsWith("[") && objIdent.endsWith("]"))
104 {
105 auto numListStr = objIdent.substring(1, objIdent.length());
106 juce::StringArray nsa;
107 nsa.addTokens(numListStr, "-", "");
108 jassert(2 == nsa.size());
109 auto startNum = nsa[0].getIntValue();
110 auto endNum = nsa[1].getIntValue();
111 objNums.reserve(endNum - startNum + 1);
112 for (auto i = startNum; i <= endNum; i++)
113 objNums.push_back(i);
114 }
115 else
116 {
117 DBG(juce::String(__FUNCTION__) + " OSC address ident " + objIdent + " not supported.");
118 }
119 }
120
121 return objNums;
122}
123
125{
126//#define TESTING
127#ifdef TESTING
128 switch (param.type)
129 {
131 DBG(juce::String(__FUNCTION__) << " type X: " << ADMOSController::ADMOSCParameterX(param).getParameterVal());
132 break;
134 DBG(juce::String(__FUNCTION__) << " type Y: " << ADMOSController::ADMOSCParameterY(param).getParameterVal());
135 break;
137 DBG(juce::String(__FUNCTION__) << " type Z: " << ADMOSController::ADMOSCParameterZ(param).getParameterVal());
138 break;
140 {
142 DBG(juce::String(__FUNCTION__) << " type XY: " << std::get<0>(xyVals) << " " << std::get<1>(xyVals));
143 }
144 break;
146 {
148 DBG(juce::String(__FUNCTION__) << " type XYZ: " << std::get<0>(xyzVals) << " " << std::get<1>(xyzVals) << " " << std::get<2>(xyzVals));
149 }
150 break;
152 DBG(juce::String(__FUNCTION__) << " type Width: " << ADMOSController::ADMOSCParameterWidth(param).getParameterVal());
153 break;
155 DBG(juce::String(__FUNCTION__) << " type Mute: " << ADMOSController::ADMOSCParameterMute(param).getParameterVal01());
156 break;
158 default:
159 DBG(juce::String(__FUNCTION__) + " type NONE unsupported.");
160 jassertfalse;
161 break;
162 }
163#endif
164
166 {
167 jassertfalse;
168 return;
169 }
170 else
171 {
172 {
173 std::lock_guard<std::mutex> l(m_objCacheMutex);
174 m_objCache[objNum][param.type] = param;
175 }
177 }
178}
179
180void ADMOSController::handleMessage(const Message& message)
181{
182 if (const ADMOSCParameterChangedMessage* apcm = dynamic_cast<const ADMOSCParameterChangedMessage*>(&message))
183 {
185 onParameterChanged(apcm->getObjNum(), apcm->getType());
186 else if (ADMOSCParameterChangeTarget::External == apcm->getTarget())
187 sendParameterChange(apcm->getObjNum(), getParameter(apcm->getObjNum(), apcm->getType()));
188 }
189}
190
192{
193 if (ADMOSController::ADMOSCParameterType::Empty == type || 0 == m_objCache.count(objNum) || 0 == m_objCache[objNum].count(type))
194 {
195 jassertfalse;
196 return {};
197 }
198 else
199 {
200 auto parameter = ADMOSCParameter();
201 {
202 std::lock_guard<std::mutex> l(m_objCacheMutex);
203 parameter = m_objCache[objNum][type];
204 }
205 return parameter;
206 }
207}
208
210{
211 if (m_oscSender)
212 {
213#ifdef DEBUG
214 auto msg = getParameterAsOSCMessage(objNum, param);
215 juce::String msgAsStr = msg.getAddressPattern().toString() + " ";
216 for (auto const& arg : msg)
217 switch (arg.getType())
218 {
219 case 'i':
220 msgAsStr += juce::String(arg.getInt32()) + " ";
221 break;
222 case 'f':
223 msgAsStr += juce::String(arg.getFloat32()) + " ";
224 break;
225 default:
226 break;
227 }
228 DBG(juce::String(__FUNCTION__) + " " + msgAsStr);
229 return m_oscSender->send(msg);
230#else
231 return m_oscSender->send(getParameterAsOSCMessage(objNum, param));
232#endif
233 }
234 else
235 return false;
236}
237
239{
240 switch (param.type)
241 {
243 return juce::OSCMessage(juce::OSCAddressPattern(s_admObjDomainStr + juce::String(objNum) + s_xStr), ADMOSCParameterX(param).getParameterVal());
245 return juce::OSCMessage(juce::OSCAddressPattern(s_admObjDomainStr + juce::String(objNum) + s_yStr), ADMOSCParameterY(param).getParameterVal());
247 return juce::OSCMessage(juce::OSCAddressPattern(s_admObjDomainStr + juce::String(objNum) + s_zStr), ADMOSCParameterZ(param).getParameterVal());
249 {
250 auto xyVal = ADMOSCParameterXY(param).getParameterVals();
251 return juce::OSCMessage(juce::OSCAddressPattern(s_admObjDomainStr + juce::String(objNum) + s_xyStr), std::get<0>(xyVal), std::get<1>(xyVal));
252 }
254 {
255 auto xyzVal = ADMOSCParameterXYZ(param).getParameterVals();
256 return juce::OSCMessage(juce::OSCAddressPattern(s_admObjDomainStr + juce::String(objNum) + s_xyzStr), std::get<0>(xyzVal), std::get<1>(xyzVal), std::get<2>(xyzVal));
257 }
259 return juce::OSCMessage(juce::OSCAddressPattern(s_admObjDomainStr + juce::String(objNum) + s_widthStr), ADMOSCParameterWidth(param).getParameterVal());
261 return juce::OSCMessage(juce::OSCAddressPattern(s_admObjDomainStr + juce::String(objNum) + s_muteStr), ADMOSCParameterMute(param).getParameterVal01());
263 default:
264 jassertfalse;
265 return juce::OSCMessage(juce::OSCAddressPattern(s_admObjDomainStr + juce::String(objNum)));
266 }
267}
268
269void ADMOSController::oscMessageReceived(const juce::OSCMessage& message)
270{
271 if (message.isEmpty())
272 return;
273
274 auto addrStr = message.getAddressPattern().toString();
275 if (addrStr.startsWith(s_admObjDomainStr))
276 {
277 auto addrContents = addrStr.fromFirstOccurrenceOf(s_admObjDomainStr, false, false);
278
279 auto objIdent = addrContents.upToFirstOccurrenceOf("/", false, false);
280 auto objNums = getObjNumsFromObjIdent(objIdent);
281
282 ADMOSCParameter param;
283 if (addrContents.endsWith(s_xStr) && 1 == message.size())
284 param = ADMOSCParameterX(message[0].getFloat32());
285 else if (addrContents.endsWith(s_yStr) && 1 == message.size())
286 param = ADMOSCParameterY(message[0].getFloat32());
287 else if (addrContents.endsWith(s_zStr) && 1 == message.size())
288 param = ADMOSCParameterZ(message[0].getFloat32());
289 else if (addrContents.endsWith(s_xyStr) && 2 == message.size())
290 param = ADMOSCParameterXY(message[0].getFloat32(), message[1].getFloat32());
291 else if (addrContents.endsWith(s_xyzStr) && 3 == message.size())
292 param = ADMOSCParameterXYZ(message[0].getFloat32(), message[1].getFloat32(), message[2].getFloat32());
293 else if (addrContents.endsWith(s_widthStr) && 1 == message.size())
294 param = ADMOSCParameterWidth(message[0].getFloat32());
295 else if (addrContents.endsWith(s_muteStr) && 1 == message.size())
296 param = ADMOSCParameterMute(message[0].getInt32());
297
298 for (auto const& objNum : objNums)
300 }
301 else
302 {
303 DBG(juce::String(__FUNCTION__) + " unhandled OSC message: " + addrStr + "(" + juce::String(message.size()) + " args)");
304 }
305}
306
307void ADMOSController::oscBundleReceived(const juce::OSCBundle& bundle)
308{
309 if (bundle.isEmpty())
310 return;
311 else
312 {
313 for (auto const& element : bundle)
314 {
315 if (element.isBundle())
316 oscBundleReceived(element.getBundle());
317 else if (element.isMessage())
318 oscMessageReceived(element.getMessage());
319 }
320 }
321}
322
323
324}
static bool createAndPostIfNotAlreadyPending(int objNum, std::uint16_t type, ADMOSCParameterChangeTarget target, ADMOSController *postTarget)
ADMOSCParameter getParameter(int objNum, std::uint16_t type)
bool startConnection(int oscPort, juce::IPAddress targetIP, int targetPort)
void handleMessage(const Message &message) override
void oscBundleReceived(const juce::OSCBundle &bundle) override
const std::vector< int > getObjNumsFromObjIdent(const juce::String &objIdent)
void setNumObjects(int numObjects)
bool sendParameterChange(int objNum, const ADMOSController::ADMOSCParameter &param)
void setParameter(int objNum, const ADMOSCParameter &param, const ADMOSCParameterChangeTarget &pct=ADMOSCParameterChangeTarget::None)
std::function< void(int, std::uint16_t)> onParameterChanged
void oscMessageReceived(const juce::OSCMessage &message) override
const juce::OSCMessage getParameterAsOSCMessage(int objNum, const ADMOSController::ADMOSCParameter &param)
Definition Mema.cpp:27
std::tuple< float, float, float > getParameterVals()
std::tuple< float, float > getParameterVals()