1 /*
2  * Carla Plugin Host
3  * Copyright (C) 2011-2020 Filipe Coelho <falktx@falktx.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of
8  * the License, or any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * For a full copy of the GNU General Public License see the doc/GPL.txt file.
16  */
17 
18 #include "CarlaEngineClient.hpp"
19 #include "CarlaEngineUtils.hpp"
20 
21 #include "CarlaString.hpp"
22 
23 CARLA_BACKEND_START_NAMESPACE
24 
25 // -----------------------------------------------------------------------
26 
_getUniquePortName(CarlaString & sname,const CarlaStringList & list)27 static void _getUniquePortName(CarlaString& sname, const CarlaStringList& list)
28 {
29     for (CarlaStringList::Itenerator it = list.begin2(); it.valid(); it.next())
30     {
31         const char* const portName(it.getValue(nullptr));
32         CARLA_SAFE_ASSERT_CONTINUE(portName != nullptr && portName[0] != '\0');
33 
34         // Check if unique name doesn't exist
35           if (sname != portName)
36               continue;
37 
38         // Check if string has already been modified
39         {
40             const std::size_t len(sname.length());
41 
42             // 1 digit, ex: " (2)"
43             if (sname[len-4] == ' ' && sname[len-3] == '(' && sname.isDigit(len-2) && sname[len-1] == ')')
44             {
45                 const int number = sname[len-2] - '0';
46 
47                 if (number == 9)
48                 {
49                     // next number is 10, 2 digits
50                     sname.truncate(len-4);
51                     sname += " (10)";
52                     //sname.replace(" (9)", " (10)");
53                 }
54                 else
55                     sname[len-2] = char('0' + number + 1);
56 
57                 continue;
58             }
59 
60             // 2 digits, ex: " (11)"
61             if (sname[len-5] == ' ' && sname[len-4] == '(' && sname.isDigit(len-3) && sname.isDigit(len-2) && sname[len-1] == ')')
62             {
63                 char n2 = sname[len-2];
64                 char n3 = sname[len-3];
65 
66                 if (n2 == '9')
67                 {
68                     n2 = '0';
69                     n3 = static_cast<char>(n3 + 1);
70                 }
71                 else
72                     n2 = static_cast<char>(n2 + 1);
73 
74                 sname[len-2] = n2;
75                 sname[len-3] = n3;
76 
77                 continue;
78             }
79         }
80 
81         // Modify string if not
82         sname += " (2)";
83     }
84 }
85 
86 // -----------------------------------------------------------------------
87 // Carla Engine Client
88 
89 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
ProtectedData(const CarlaEngine & eng,EngineInternalGraph & eg,const CarlaPluginPtr p)90 CarlaEngineClient::ProtectedData::ProtectedData(const CarlaEngine& eng,
91                                                 EngineInternalGraph& eg,
92                                                 const CarlaPluginPtr p) noexcept
93 #else
94 CarlaEngineClient::ProtectedData::ProtectedData(const CarlaEngine& eng) noexcept
95 #endif
96     :  engine(eng),
97        active(false),
98        latency(0),
99 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
100        cvSourcePorts(),
101        egraph(eg),
102        plugin(p),
103 #endif
104        audioInList(),
105        audioOutList(),
106        cvInList(),
107        cvOutList(),
108        eventInList(),
109        eventOutList() {}
110 
~ProtectedData()111 CarlaEngineClient::ProtectedData::~ProtectedData()
112 {
113     carla_debug("CarlaEngineClient::ProtectedData::~ProtectedData()");
114 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
115     CARLA_SAFE_ASSERT(plugin.get() == nullptr);
116 #endif
117 }
118 
addAudioPortName(const bool isInput,const char * const name)119 void CarlaEngineClient::ProtectedData::addAudioPortName(const bool isInput, const char* const name)
120 {
121     CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);
122 
123     CarlaStringList& portList(isInput ? audioInList : audioOutList);
124     portList.append(name);
125 }
126 
addCVPortName(const bool isInput,const char * const name)127 void CarlaEngineClient::ProtectedData::addCVPortName(const bool isInput, const char* const name)
128 {
129     CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);
130 
131     CarlaStringList& portList(isInput ? cvInList : cvOutList);
132     portList.append(name);
133 }
134 
addEventPortName(const bool isInput,const char * const name)135 void CarlaEngineClient::ProtectedData::addEventPortName(const bool isInput, const char* const name)
136 {
137     CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0',);
138 
139     CarlaStringList& portList(isInput ? eventInList : eventOutList);
140     portList.append(name);
141 }
142 
getUniquePortName(const char * const name)143 const char* CarlaEngineClient::ProtectedData::getUniquePortName(const char* const name)
144 {
145     CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', nullptr);
146 
147     CarlaString sname;
148     sname = name;
149 
150     _getUniquePortName(sname, audioInList);
151     _getUniquePortName(sname, audioOutList);
152     _getUniquePortName(sname, cvInList);
153     _getUniquePortName(sname, cvOutList);
154     _getUniquePortName(sname, eventInList);
155     _getUniquePortName(sname, eventOutList);
156 
157     return sname.dup();
158 }
159 
clearPorts()160 void CarlaEngineClient::ProtectedData::clearPorts()
161 {
162     audioInList.clear();
163     audioOutList.clear();
164     cvInList.clear();
165     cvOutList.clear();
166     eventInList.clear();
167     eventOutList.clear();
168 }
169 
170 // -----------------------------------------------------------------------
171 
CarlaEngineClient(ProtectedData * const p)172 CarlaEngineClient::CarlaEngineClient(ProtectedData* const p)
173     : pData(p)
174 {
175     carla_debug("CarlaEngineClient::CarlaEngineClient()");
176 }
177 
~CarlaEngineClient()178 CarlaEngineClient::~CarlaEngineClient() noexcept
179 {
180     carla_debug("CarlaEngineClient::~CarlaEngineClient()");
181 }
182 
activate()183 void CarlaEngineClient::activate() noexcept
184 {
185     CARLA_SAFE_ASSERT(! pData->active);
186     carla_debug("CarlaEngineClient::activate()");
187 
188     pData->active = true;
189 }
190 
deactivate(const bool willClose)191 void CarlaEngineClient::deactivate(const bool willClose) noexcept
192 {
193     CARLA_SAFE_ASSERT(pData->active || willClose);
194     carla_debug("CarlaEngineClient::deactivate(%s)", bool2str(willClose));
195 
196     pData->active = false;
197 
198     if (willClose)
199     {
200 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
201         pData->cvSourcePorts.resetGraphAndPlugin();
202         pData->plugin.reset();
203 #endif
204     }
205 }
206 
isActive() const207 bool CarlaEngineClient::isActive() const noexcept
208 {
209     return pData->active;
210 }
211 
isOk() const212 bool CarlaEngineClient::isOk() const noexcept
213 {
214     return true;
215 }
216 
getLatency() const217 uint32_t CarlaEngineClient::getLatency() const noexcept
218 {
219     return pData->latency;
220 }
221 
setLatency(const uint32_t samples)222 void CarlaEngineClient::setLatency(const uint32_t samples) noexcept
223 {
224     pData->latency = samples;
225 }
226 
addPort(const EnginePortType portType,const char * const name,const bool isInput,const uint32_t indexOffset)227 CarlaEnginePort* CarlaEngineClient::addPort(const EnginePortType portType, const char* const name, const bool isInput, const uint32_t indexOffset)
228 {
229     CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', nullptr);
230     carla_debug("CarlaEngineClient::addPort(%i:%s, \"%s\", %s, %u)", portType, EnginePortType2Str(portType), name, bool2str(isInput), indexOffset);
231 
232     switch (portType)
233     {
234     case kEnginePortTypeNull:
235         break;
236     case kEnginePortTypeAudio:
237         pData->addAudioPortName(isInput, name);
238         return new CarlaEngineAudioPort(*this, isInput, indexOffset);
239     case kEnginePortTypeCV:
240         pData->addCVPortName(isInput, name);
241         return new CarlaEngineCVPort(*this, isInput, indexOffset);
242     case kEnginePortTypeEvent:
243         pData->addEventPortName(isInput, name);
244         return new CarlaEngineEventPort(*this, isInput, indexOffset);
245     }
246 
247     carla_stderr("CarlaEngineClient::addPort(%i, \"%s\", %s) - invalid type", portType, name, bool2str(isInput));
248     return nullptr;
249 }
250 
removePort(const EnginePortType portType,const char * const name,const bool isInput)251 bool CarlaEngineClient::removePort(const EnginePortType portType, const char* const name, const bool isInput)
252 {
253     CARLA_SAFE_ASSERT_RETURN(name != nullptr && name[0] != '\0', false);
254     carla_debug("CarlaEngineClient::removePort(%i:%s, \"%s\", %s)", portType, EnginePortType2Str(portType), name, bool2str(isInput));
255 
256     switch (portType)
257     {
258     case kEnginePortTypeNull:
259         break;
260     case kEnginePortTypeAudio: {
261         CarlaStringList& portList(isInput ? pData->audioInList : pData->audioOutList);
262         portList.append(name);
263         return portList.removeOne(name);
264     }
265     case kEnginePortTypeCV: {
266         CarlaStringList& portList(isInput ? pData->cvInList : pData->cvOutList);
267         return portList.removeOne(name);
268     }
269     case kEnginePortTypeEvent: {
270         CarlaStringList& portList(isInput ? pData->eventInList : pData->eventOutList);
271         return portList.removeOne(name);
272     }
273     }
274 
275     return false;
276 }
277 
278 #ifndef BUILD_BRIDGE_ALTERNATIVE_ARCH
createCVSourcePorts()279 CarlaEngineCVSourcePorts* CarlaEngineClient::createCVSourcePorts()
280 {
281     pData->cvSourcePorts.setGraphAndPlugin(pData->egraph.getPatchbayGraphOrNull(), pData->plugin);
282     return &pData->cvSourcePorts;
283 }
284 #endif
285 
getEngine() const286 const CarlaEngine& CarlaEngineClient::getEngine() const noexcept
287 {
288     return pData->engine;
289 }
290 
getProcessMode() const291 EngineProcessMode CarlaEngineClient::getProcessMode() const noexcept
292 {
293     return pData->engine.getProccessMode();
294 }
295 
getPortCount(const EnginePortType portType,const bool isInput) const296 uint CarlaEngineClient::getPortCount(const EnginePortType portType, const bool isInput) const noexcept
297 {
298     size_t ret = 0;
299 
300     switch (portType)
301     {
302     case kEnginePortTypeNull:
303         break;
304     case kEnginePortTypeAudio:
305         ret = isInput ? pData->audioInList.count() : pData->audioOutList.count();
306         break;
307     case kEnginePortTypeCV:
308         ret = isInput ? pData->cvInList.count() : pData->cvOutList.count();
309         break;
310     case kEnginePortTypeEvent:
311         ret = isInput ? pData->eventInList.count() : pData->eventOutList.count();
312         break;
313     }
314 
315     return static_cast<uint>(ret);
316 }
317 
getAudioPortName(const bool isInput,const uint index) const318 const char* CarlaEngineClient::getAudioPortName(const bool isInput, const uint index) const noexcept
319 {
320     CarlaStringList& portList(isInput ? pData->audioInList : pData->audioOutList);
321     CARLA_SAFE_ASSERT_RETURN(index < portList.count(), nullptr);
322 
323     return portList.getAt(index);
324 }
325 
getCVPortName(const bool isInput,const uint index) const326 const char* CarlaEngineClient::getCVPortName(const bool isInput, const uint index) const noexcept
327 {
328     CarlaStringList& portList(isInput ? pData->cvInList : pData->cvOutList);
329     CARLA_SAFE_ASSERT_RETURN(index < portList.count(), nullptr);
330 
331     return portList.getAt(index);
332 }
333 
getEventPortName(const bool isInput,const uint index) const334 const char* CarlaEngineClient::getEventPortName(const bool isInput, const uint index) const noexcept
335 {
336     CarlaStringList& portList(isInput ? pData->eventInList : pData->eventOutList);
337     CARLA_SAFE_ASSERT_RETURN(index < portList.count(), nullptr);
338 
339     return portList.getAt(index);
340 }
341 
342 // -----------------------------------------------------------------------
343 
344 CARLA_BACKEND_END_NAMESPACE
345