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