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