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: 08 окт. 2015 г. 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 <metadata/plugins.h> 23 #include <metadata/ports.h> 24 #include <core/debug.h> 25 #include <core/alloc.h> 26 27 #include <stdio.h> 28 #include <math.h> 29 #include <locale.h> 30 #include <errno.h> 31 #include <stdlib.h> 32 33 #define UPDATE_LOCALE(out_var, lc, value) \ 34 char *out_var = ::setlocale(lc, NULL); \ 35 if (out_var != NULL) \ 36 { \ 37 size_t ___len = ::strlen(out_var) + 1; \ 38 char *___copy = static_cast<char *>(::alloca(___len)); \ 39 ::memcpy(___copy, out_var, ___len); \ 40 out_var = ___copy; \ 41 } \ 42 ::setlocale(lc, value); 43 44 namespace lsp 45 { 46 const port_t lv2_atom_ports[] = 47 { 48 // Input audio ports 49 { LSP_LV2_ATOM_PORT_IN, "UI Input", U_NONE, R_UI_SYNC, F_IN, 0, 0, 0, 0, NULL }, 50 { LSP_LV2_ATOM_PORT_OUT, "UI Output", U_NONE, R_UI_SYNC, F_OUT, 0, 0, 0, 0, NULL }, 51 52 { NULL, NULL } 53 }; 54 55 const port_t lv2_latency_port = 56 { 57 LSP_LV2_LATENCY_PORT, "Latency OUT", U_NONE, R_CONTROL, F_OUT | F_INT | F_LOWER | F_UPPER, 0, MAX_SAMPLE_RATE, 0, 0, NULL 58 }; 59 60 typedef struct unit_desc_t 61 { 62 const char *name; 63 const char *lc_key; 64 } unit_desc_t; 65 66 const unit_desc_t unit_desc[] = 67 { 68 { NULL, NULL }, 69 { NULL, NULL }, 70 { NULL, NULL }, 71 { "%", "units.pc" }, 72 73 { "mm", "units.mm" }, 74 { "cm", "units.cm" }, 75 { "m", "units.m" }, 76 { "\"", "units.inch" }, 77 { "km", "units.km" }, 78 79 { "m/s", "units.mps" }, 80 { "km/h", "units.kmph" }, 81 82 { "samp", "units.samp" }, 83 84 { "Hz", "units.hz" }, 85 { "kHz", "units.khz" }, 86 { "MHz", "units.mhz" }, 87 { "bpm", "units.bpm" }, 88 89 { "cent", "units.cent" }, 90 { "oct", "units.octave" }, 91 { "st", "units.st" }, 92 93 { "bar", "units.bar" }, 94 { "beat", "units.beat" }, 95 { "min", "units.min" }, 96 { "s", "units.s" }, 97 { "ms", "units.ms" }, 98 99 { "dB", "units.db" }, 100 { "G", "units.gain" }, 101 { "G", "units.gain" }, 102 103 { "°", "units.deg" }, 104 { "°C", "units.degc" }, 105 { "°F", "units.degf" }, 106 { "°K", "units.degk" }, 107 { "°R", "units.degr" }, 108 109 { "B", "units.bytes" }, 110 { "kB", "units.kbytes" }, 111 { "MB", "units.mbytes" }, 112 { "GB", "units.gbytes" }, 113 { "TB", "units.tbytes" }, 114 115 NULL 116 }; 117 118 static port_item_t default_bool[] = 119 { 120 { "off", "bool.off" }, 121 { "on", "bool.on" }, 122 { NULL, NULL } 123 }; 124 encode_unit(size_t unit)125 const char *encode_unit(size_t unit) 126 { 127 return ((unit >= 0) && (unit <= U_ENUM)) ? 128 unit_desc[unit].name : NULL; 129 } 130 unit_lc_key(size_t unit)131 const char *unit_lc_key(size_t unit) 132 { 133 return ((unit >= 0) && (unit <= U_ENUM)) ? 134 unit_desc[unit].lc_key : NULL; 135 } 136 decode_unit(const char * name)137 unit_t decode_unit(const char *name) 138 { 139 for (ssize_t i=0; i<= U_ENUM; ++i) 140 { 141 const char *uname = unit_desc[i].name; 142 if ((uname != NULL) && (!::strcmp(name, uname))) 143 return unit_t(i); 144 } 145 return U_NONE; 146 } 147 is_discrete_unit(size_t unit)148 bool is_discrete_unit(size_t unit) 149 { 150 switch (unit) 151 { 152 case U_BOOL: 153 case U_SAMPLES: 154 case U_ENUM: 155 return true; 156 default: 157 break; 158 } 159 return false; 160 } 161 is_decibel_unit(size_t unit)162 bool is_decibel_unit(size_t unit) 163 { 164 switch (unit) 165 { 166 case U_DB: 167 case U_GAIN_AMP: 168 case U_GAIN_POW: 169 return true; 170 default: 171 break; 172 } 173 return false; 174 } 175 is_gain_unit(size_t unit)176 bool is_gain_unit(size_t unit) 177 { 178 switch (unit) 179 { 180 case U_GAIN_AMP: 181 case U_GAIN_POW: 182 return true; 183 default: 184 break; 185 } 186 return false; 187 } 188 is_degree_unit(size_t unit)189 bool is_degree_unit(size_t unit) 190 { 191 switch (unit) 192 { 193 case U_DEG: 194 case U_DEG_CEL: 195 case U_DEG_FAR: 196 case U_DEG_K: 197 case U_DEG_R: 198 return true; 199 default: 200 break; 201 } 202 return false; 203 } 204 is_log_rule(const port_t * port)205 bool is_log_rule(const port_t *port) 206 { 207 if (port->flags & F_LOG) 208 return true; 209 return is_decibel_unit(port->unit); 210 } 211 list_size(const port_item_t * list)212 size_t list_size(const port_item_t *list) 213 { 214 size_t size = 0; 215 for ( ; (list != NULL) && (list->text != NULL); ++list) 216 ++size; 217 return size; 218 } 219 limit_value(const port_t * port,float value)220 float limit_value(const port_t *port, float value) 221 { 222 if ((port->flags & (F_CYCLIC | F_UPPER | F_LOWER)) == (F_CYCLIC | F_UPPER | F_LOWER)) 223 { 224 if (port->max > port->min) 225 { 226 value = port->min + fmodf(value - port->min, port->max - port->min); 227 if (value < port->min) 228 value += port->max - port->min; 229 } 230 else if (port->min > port->max) 231 { 232 value = port->max + fmodf(value - port->max, port->min - port->max); 233 if (value < port->max) 234 value += port->min - port->max; 235 } 236 } 237 238 if (port->flags & F_UPPER) 239 { 240 if (value > port->max) 241 value = port->max; 242 } 243 if (port->flags & F_LOWER) 244 { 245 if (value < port->min) 246 value = port->min; 247 } 248 return value; 249 } 250 format_float(char * buf,size_t len,const port_t * meta,float value,ssize_t precision)251 void format_float(char *buf, size_t len, const port_t *meta, float value, ssize_t precision) 252 { 253 float v = (value < 0.0f) ? - value : value; 254 size_t tolerance = 0; 255 256 // Select the tolerance of output value 257 if (precision < 0) 258 { 259 // Determine regular tolerance 260 if (v < 0.1f) 261 tolerance = 4; 262 else if (v < 1.0f) 263 tolerance = 3; 264 else if (v < 10.0f) 265 tolerance = 2; 266 else if (v < 100.0f) 267 tolerance = 1; 268 else 269 tolerance = 0; 270 271 // Now determine normal tolerance 272 if (meta->flags & F_STEP) 273 { 274 size_t max_tol = 0; 275 float step = (meta->step < 0.0f) ? - meta->step : meta->step; 276 while ((max_tol < 4) && (truncf(step) <= 0)) 277 { 278 step *= 10; 279 max_tol++; 280 } 281 282 if (tolerance > max_tol) 283 tolerance = max_tol; 284 } 285 } 286 else 287 tolerance = (precision > 4) ? 4 : precision; 288 289 const char *fmt = "%.0f"; 290 switch (tolerance) 291 { 292 case 4: fmt = "%.4f"; break; 293 case 3: fmt = "%.3f"; break; 294 case 2: fmt = "%.2f"; break; 295 case 1: fmt = "%.1f"; break; 296 default: fmt = "%.0f"; break; 297 }; 298 299 snprintf(buf, len, fmt, value); 300 buf[len - 1] = '\0'; 301 } 302 format_int(char * buf,size_t len,const port_t * meta,float value)303 void format_int(char *buf, size_t len, const port_t *meta, float value) 304 { 305 snprintf(buf, len, "%ld", long(value)); 306 buf[len - 1] = '\0'; 307 } 308 format_enum(char * buf,size_t len,const port_t * meta,float value)309 void format_enum(char *buf, size_t len, const port_t *meta, float value) 310 { 311 float min = (meta->flags & F_LOWER) ? meta->min: 0; 312 float step = (meta->flags & F_STEP) ? meta->step : 1.0; 313 314 for (const port_item_t *p = meta->items; (p != NULL) && (p->text != NULL); ++p) 315 { 316 if (min >= value) 317 { 318 ::strncpy(buf, p->text, len); 319 buf[len - 1] = '\0'; 320 return; 321 } 322 min += step; 323 } 324 buf[0] = '\0'; 325 } 326 format_decibels(char * buf,size_t len,const port_t * meta,float value,ssize_t precision)327 void format_decibels(char *buf, size_t len, const port_t *meta, float value, ssize_t precision) 328 { 329 double mul = (meta->unit == U_GAIN_AMP) ? 20.0 : 10.0; 330 if (value < 0.0f) 331 value = - value; 332 333 value = mul * log(value) / M_LN10; 334 float thresh = (meta->flags & F_EXT) ? -140.0f : -80.0f; 335 if (value <= thresh) 336 { 337 strcpy(buf, "-inf"); 338 return; 339 } 340 341 const char *fmt; 342 if (precision < 0) 343 fmt = "%.2f"; 344 else if (precision == 1) 345 fmt = "%.1f"; 346 else if (precision == 2) 347 fmt = "%.2f"; 348 else if (precision == 3) 349 fmt = "%.3f"; 350 else 351 fmt = "%.4f"; 352 353 snprintf(buf, len, fmt, value); 354 buf[len - 1] = '\0'; 355 } 356 format_bool(char * buf,size_t len,const port_t * meta,float value)357 void format_bool(char *buf, size_t len, const port_t *meta, float value) 358 { 359 const port_item_t *list = (meta->items != NULL) ? meta->items : default_bool; 360 if (value >= 0.5f) 361 ++list; 362 363 if (list->text != NULL) 364 { 365 ::strncpy(buf, list->text, len); 366 buf[len-1] = '\0'; 367 } 368 else 369 buf[0] = '\0'; 370 } 371 format_value(char * buf,size_t len,const port_t * meta,float value,ssize_t precision)372 void format_value(char *buf, size_t len, const port_t *meta, float value, ssize_t precision) 373 { 374 if (meta->unit == U_BOOL) 375 format_bool(buf, len, meta, value); 376 else if (meta->unit == U_ENUM) 377 format_enum(buf, len, meta, value); 378 else if ((meta->unit == U_GAIN_AMP) || (meta->unit == U_GAIN_POW)) 379 format_decibels(buf, len, meta, value, precision); 380 else if (meta->flags & F_INT) 381 format_int(buf, len, meta, value); 382 else 383 format_float(buf, len, meta, value, precision); 384 } 385 parse_bool(float * dst,const char * text)386 status_t parse_bool(float *dst, const char *text) 387 { 388 if ((!::strcasecmp(text, "true")) || 389 (!::strcasecmp(text, "on")) || 390 (!::strcasecmp(text, "1"))) 391 { 392 if (dst != NULL) 393 *dst = 1.0f; 394 return STATUS_OK; 395 } 396 397 if ((!::strcasecmp(text, "false")) || 398 (!::strcasecmp(text, "off")) || 399 (!::strcasecmp(text, "0"))) 400 { 401 if (dst != NULL) 402 *dst = 0.0f; 403 return STATUS_OK; 404 } 405 406 return STATUS_INVALID_VALUE; 407 } 408 parse_enum(float * dst,const char * text,const port_t * meta)409 status_t parse_enum(float *dst, const char *text, const port_t *meta) 410 { 411 float min = (meta->flags & F_LOWER) ? meta->min: 0; 412 float step = (meta->flags & F_STEP) ? meta->step : 1.0; 413 414 for (const port_item_t *p = meta->items; (p != NULL) && (p->text != NULL); ++p) 415 { 416 if (!::strcasecmp(text, p->text)) 417 { 418 if (dst != NULL) 419 *dst = min; 420 return STATUS_OK; 421 } 422 min += step; 423 } 424 425 return STATUS_INVALID_VALUE; 426 } 427 parse_decibels(float * dst,const char * text,const port_t * meta)428 status_t parse_decibels(float *dst, const char *text, const port_t *meta) 429 { 430 if (!::strcasecmp(text, "-inf")) 431 { 432 if (dst != NULL) 433 *dst = 0.0f; 434 return STATUS_OK; 435 } 436 437 float mul = (meta->unit == U_GAIN_AMP) ? 0.05f : 0.1f; 438 439 // Parse float value 440 UPDATE_LOCALE(saved_locale, LC_NUMERIC, "C"); 441 errno = 0; 442 char *end = NULL; 443 float value = ::strtof(text, &end); 444 status_t res= STATUS_INVALID_VALUE; 445 446 if ((*end == '\0') && (errno == 0)) 447 { 448 if (dst != NULL) 449 *dst = ::expf(value * M_LN10 * mul); 450 res = STATUS_OK; 451 } 452 453 if (saved_locale != NULL) 454 ::setlocale(LC_NUMERIC, saved_locale); 455 456 return res; 457 } 458 parse_int(float * dst,const char * text,const port_t * meta)459 status_t parse_int(float *dst, const char *text, const port_t *meta) 460 { 461 errno = 0; 462 char *end = NULL; 463 long value = ::strtol(text, &end, 10); 464 if ((*end == '\0') && (errno == 0)) 465 { 466 if (dst != NULL) 467 *dst = value; 468 return STATUS_OK; 469 } 470 471 return STATUS_INVALID_VALUE; 472 } 473 parse_float(float * dst,const char * text,const port_t * meta)474 status_t parse_float(float *dst, const char *text, const port_t *meta) 475 { 476 // Parse float value 477 UPDATE_LOCALE(saved_locale, LC_NUMERIC, "C"); 478 errno = 0; 479 char *end = NULL; 480 float value = ::strtof(text, &end); 481 status_t res= STATUS_INVALID_VALUE; 482 483 if ((*end == '\0') && (errno == 0)) 484 { 485 if (dst != NULL) 486 *dst = value; 487 res = STATUS_OK; 488 } 489 490 if (saved_locale != NULL) 491 ::setlocale(LC_NUMERIC, saved_locale); 492 493 return res; 494 } 495 parse_value(float * dst,const char * text,const port_t * meta)496 status_t parse_value(float *dst, const char *text, const port_t *meta) 497 { 498 if ((text == NULL) || (meta == NULL) || (*text == '\0')) 499 return STATUS_BAD_ARGUMENTS; 500 501 if (meta->unit == U_BOOL) 502 return parse_bool(dst, text); 503 else if (meta->unit == U_ENUM) 504 return parse_enum(dst, text, meta); 505 else if ((meta->unit == U_GAIN_AMP) || (meta->unit == U_GAIN_POW)) 506 return parse_decibels(dst, text, meta); 507 else if (meta->flags & F_INT) 508 return parse_int(dst, text, meta); 509 else 510 return parse_float(dst, text, meta); 511 512 return STATUS_BAD_ARGUMENTS; 513 } 514 get_port_parameters(const port_t * p,float * min,float * max,float * step)515 void get_port_parameters(const port_t *p, float *min, float *max, float *step) 516 { 517 float f_min = 0.0f, f_max = 1.0f, f_step = 0.001f; 518 519 if (p->unit == U_BOOL) 520 { 521 f_min = 0.0f; 522 f_max = 1.0f; 523 f_step = 1.0f; 524 } 525 else if (p->unit == U_ENUM) 526 { 527 f_min = (p->flags & F_LOWER) ? p->min : 0.0f; 528 f_max = f_min + list_size(p->items) - 1; 529 f_step = 1.0f; 530 } 531 else if (p->unit == U_SAMPLES) 532 { 533 f_min = p->min; 534 f_max = p->max; 535 f_step = 1.0f; 536 } 537 else 538 { 539 f_min = (p->flags & F_LOWER) ? p->min : 0.0f; 540 f_max = (p->flags & F_UPPER) ? p->max : 1.0f; 541 542 if (p->flags & F_INT) 543 f_step = (p->flags & F_STEP) ? p->step : 1.0f; 544 else 545 f_step = (p->flags & F_STEP) ? p->step : (f_max - f_min) * 0.001; 546 } 547 548 if (min != NULL) 549 *min = f_min; 550 if (max != NULL) 551 *max = f_max; 552 if (step != NULL) 553 *step = f_step; 554 } 555 clone_port_metadata(const port_t * metadata,const char * postfix)556 port_t *clone_port_metadata(const port_t *metadata, const char *postfix) 557 { 558 if (metadata == NULL) 559 return NULL; 560 561 size_t postfix_len = (postfix != NULL) ? strlen(postfix) : 0; 562 size_t string_bytes = 0; 563 size_t elements = 1; // At least PORTS_END should be present 564 565 for (const port_t *p=metadata; p->id != NULL; ++p) 566 { 567 elements ++; 568 if (postfix_len > 0) 569 string_bytes += strlen(p->id) + postfix_len + 1; 570 } 571 572 // Calculate the overall allocation size 573 size_t to_copy = sizeof(port_t) * elements; 574 string_bytes = ALIGN_SIZE(string_bytes, DEFAULT_ALIGN); 575 elements = ALIGN_SIZE(to_copy, DEFAULT_ALIGN); 576 size_t allocate = string_bytes + elements; 577 uint8_t *ptr = lsp_tmalloc(uint8_t, allocate); 578 port_t *meta = reinterpret_cast<port_t *>(ptr); 579 580 // Copy port metadata 581 ::memcpy(meta, metadata, to_copy); 582 583 // Update identifiers if needed 584 if (postfix_len > 0) 585 { 586 port_t *m = meta; 587 char *dst = reinterpret_cast<char *>(ptr + elements); 588 589 for (const port_t *p=metadata; p->id != NULL; ++p, ++m) 590 { 591 m->id = dst; 592 size_t slen = strlen(p->id); 593 memcpy(dst, p->id, slen); 594 dst += slen; 595 memcpy(dst, postfix, postfix_len); 596 dst += postfix_len; 597 *(dst++) = '\0'; 598 } 599 600 lsp_paranoia 601 ( 602 ptr += allocate; 603 lsp_assert(ptr >= reinterpret_cast<uint8_t *>(dst)); 604 ); 605 } 606 607 return meta; 608 } 609 drop_port_metadata(port_t * metadata)610 void drop_port_metadata(port_t *metadata) 611 { 612 if (metadata == NULL) 613 return; 614 lsp_free(metadata); 615 } 616 port_list_size(const port_t * metadata)617 size_t port_list_size(const port_t *metadata) 618 { 619 size_t count = 0; 620 while (metadata->id != NULL) 621 { 622 count ++; 623 metadata ++; 624 } 625 return count; 626 } 627 } 628 629 630