1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 11 нояб. 2017 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <ui/ctl/ctl.h>
23 #include <ui/ui.h>
24 #include <ui/plugin_ui.h>
25 
26 namespace lsp
27 {
28     namespace ctl
29     {
CtlSwitchedPort(plugin_ui * ui)30         CtlSwitchedPort::CtlSwitchedPort(plugin_ui *ui): CtlPort(NULL)
31         {
32             pUI             = ui;
33             nDimensions     = 0;
34             vControls       = NULL;
35             pReference      = NULL;
36             sName           = NULL;
37             sTokens         = NULL;
38         }
39 
~CtlSwitchedPort()40         CtlSwitchedPort::~CtlSwitchedPort()
41         {
42             destroy();
43         }
44 
tokenize(const char * path)45         CtlSwitchedPort::token_t *CtlSwitchedPort::tokenize(const char *path)
46         {
47             buffer_t buf;
48             if (!init_buf(&buf))
49                 return NULL;
50 
51             while (path != NULL)
52             {
53                 char c = *path;
54                 if (c == '\0')
55                     return reinterpret_cast<token_t *>(buf.pString);
56 
57                 if (c == '[')
58                 {
59                     path    ++;
60                     const char *end   = strchr(path, ']');
61                     if (end == NULL)
62                     {
63                         destroy_buf(&buf);
64                         return NULL;
65                     }
66 
67                     if (!append_buf(&buf, char(TT_INDEX)))
68                         break;
69                     if (!append_buf(&buf, path, end - path))
70                         break;
71                     if (!append_buf(&buf, char('\0')))
72                         break;
73                     path    = end + 1;
74                 }
75                 else
76                 {
77                     const char *end   = strchr(path + 1, '[');
78                     if (end == NULL)
79                         end         = path + strlen(path);
80 
81                     if (!append_buf(&buf, char(TT_STRING)))
82                         break;
83                     if (!append_buf(&buf, path, end - path))
84                         break;
85                     if (!append_buf(&buf, char('\0')))
86                         break;
87                     path    = end;
88                 }
89             }
90 
91             destroy_buf(&buf);
92             return NULL;
93         }
94 
next_token(token_t * token)95         CtlSwitchedPort::token_t *CtlSwitchedPort::next_token(token_t *token)
96         {
97             if (token == NULL)
98                 return NULL;
99             size_t len  = strlen(token->data);
100             return reinterpret_cast<token_t *>(reinterpret_cast<char *>(token) + len + 2);
101         }
102 
rebind()103         void CtlSwitchedPort::rebind()
104         {
105             // Unbind from referenced ports
106             if (pReference != NULL)
107             {
108                 pReference->unbind(this);
109                 pMetadata       = NULL;
110             }
111 
112             // Initialize buffer
113             buffer_t buf;
114             if (!init_buf(&buf))
115                 return;
116 
117             // Generate port name
118             size_t ctl_id   = 0;
119             token_t *tok    = sTokens;
120             while (tok->type != TT_END)
121             {
122                 if (tok->type == TT_INDEX)
123                 {
124                     char tmp_buf[32];
125                     int index   = vControls[ctl_id]->get_value();
126                     snprintf(tmp_buf, sizeof(tmp_buf), "_%d", index);
127                     if (!append_buf(&buf, tmp_buf))
128                     {
129                         destroy_buf(&buf);
130                         return;
131                     }
132                     ctl_id      ++;
133                 }
134                 else if (tok->type == TT_STRING)
135                 {
136                     if (!append_buf(&buf, tok->data))
137                     {
138                         destroy_buf(&buf);
139                         return;
140                     }
141                 }
142                 else
143                     break;
144                 tok     = next_token(tok);
145             }
146 
147             // Now fetch port by name
148             pReference  = pUI->port(buf.pString);
149             if (pReference != NULL)
150             {
151                 pMetadata       = pReference->metadata();
152                 pReference->bind(this);
153             }
154 
155             // Destroy buffer
156             destroy_buf(&buf);
157         }
158 
destroy()159         void CtlSwitchedPort::destroy()
160         {
161             if (pReference != NULL)
162             {
163                 pReference->unbind(this);
164                 pReference  = NULL;
165             }
166             if (vControls != NULL)
167             {
168                 delete [] vControls;
169                 vControls = NULL;
170             }
171             if (sName != NULL)
172             {
173                 lsp_free(sName);
174                 sName       = NULL;
175             }
176             if (sTokens != NULL)
177             {
178                 lsp_free(sTokens);
179                 sTokens     = NULL;
180             }
181             pMetadata       = NULL;
182         }
183 
compile(const char * id)184         bool CtlSwitchedPort::compile(const char *id)
185         {
186             destroy();
187 
188             sTokens = tokenize(id);
189             if (sTokens != NULL)
190             {
191                 sName   = lsp_strdup(id);
192                 if (sName != NULL)
193                 {
194                     // Calculate number of control ports
195                     nDimensions     = 0;
196                     token_t *tok    = sTokens;
197                     while (tok->type != TT_END)
198                     {
199                         if (tok->type == TT_INDEX)
200                             nDimensions++;
201                         tok     = next_token(tok);
202                     }
203 
204                     // Bind control ports
205                     vControls       = new CtlPort *[nDimensions];
206                     if (vControls != NULL)
207                     {
208                         size_t index    = 0;
209                         tok             = sTokens;
210                         while (tok->type != TT_END)
211                         {
212                             if (tok->type == TT_INDEX)
213                             {
214                                 CtlPort *sw         = pUI->port(tok->data);
215                                 if (sw != NULL)
216                                 {
217                                     sw->bind(this);
218                                     vControls[index++]  = sw;
219                                 }
220                             }
221                             tok     = next_token(tok);
222                         }
223 
224                         rebind();
225                         return true;
226                     }
227                 }
228             }
229 
230             destroy();
231             return false;
232         }
233 
write(const void * buffer,size_t size)234         void CtlSwitchedPort::write(const void *buffer, size_t size)
235         {
236             CtlPort *p  = current();
237             if (p != NULL)
238                 p->write(buffer, size);
239         }
240 
get_buffer()241         void *CtlSwitchedPort::get_buffer()
242         {
243             CtlPort *p  = current();
244             return (p != NULL) ? p->get_buffer() : NULL;
245         }
246 
get_value()247         float CtlSwitchedPort::get_value()
248         {
249             CtlPort *p  = current();
250             return (p != NULL) ? p->get_value() : 0.0f;
251         }
252 
get_default_value()253         float CtlSwitchedPort::get_default_value()
254         {
255             CtlPort *p  = current();
256             return (p != NULL) ? p->get_default_value() : 0.0f;
257         }
258 
set_value(float value)259         void CtlSwitchedPort::set_value(float value)
260         {
261             CtlPort *p  = current();
262             if (p != NULL)
263                 p->set_value(value);
264         }
265 
notify_all()266         void CtlSwitchedPort::notify_all()
267         {
268             CtlPort *p  = current();
269             if (p != NULL)
270                 p->notify_all(); // We will receive notify() as subscribers
271             else
272                 CtlPort::notify_all();
273         }
274 
id() const275         const char *CtlSwitchedPort::id() const
276         {
277             return sName;
278         }
279 
notify(CtlPort * port)280         void CtlSwitchedPort::notify(CtlPort *port)
281         {
282             // Check that event is not from dimension-control port
283             for (size_t i=0; i<nDimensions; ++i)
284             {
285                 if (port == vControls[i])
286                 {
287                     rebind();
288                     notify_all();
289                     return;
290                 }
291             }
292 
293             // Proxy notify() event only for active port
294             CtlPort *p  = current();
295             if ((p == NULL) || (port != p))
296                 return;
297 
298             // Notify all subscribers
299             CtlPort::notify_all();
300         }
301 
302     } /* namespace ctl */
303 } /* namespace lsp */
304