Initial VST3 Support (disabled)

Disabled until Steinberg licensing compliance  can be sorted out.
This commit is contained in:
Robin Davies
2022-08-15 18:14:49 -04:00
parent 351d1542e1
commit 553ae2a3f7
56 changed files with 4840 additions and 637 deletions
+4 -1
View File
@@ -4,7 +4,10 @@
"name": "Linux", "name": "Linux",
"includePath": [ "includePath": [
"${default}", "${default}",
"/usr/include/lilv-0" "${workspaceFolder}",
"${workspaceFolder}/src",
"/usr/include/lilv-0",
"~/src/vst3sdk"
], ],
"compilerPath": "/usr/bin/gcc", "compilerPath": "/usr/bin/gcc",
"cStandard": "c17", "cStandard": "c17",
+2 -3
View File
@@ -90,9 +90,8 @@
"span_ext": "cpp", "span_ext": "cpp",
"string_span": "cpp", "string_span": "cpp",
"util": "cpp", "util": "cpp",
"charconv": "cpp",
"valarray": "cpp"
}, },
"cSpell.words": [ "cSpell.words": [
"Guitarix", "Guitarix",
+1 -1
View File
@@ -265,7 +265,7 @@ GEM
thread_safe (0.3.6) thread_safe (0.3.6)
typhoeus (1.4.0) typhoeus (1.4.0)
ethon (>= 0.9.0) ethon (>= 0.9.0)
tzinfo (1.2.9) tzinfo (1.2.10)
thread_safe (~> 0.1) thread_safe (~> 0.1)
unf (0.1.4) unf (0.1.4)
unf_ext unf_ext
Binary file not shown.
+2 -2
View File
@@ -77,7 +77,7 @@ A straighforward no-frills digital delay.
lv2:minimum 0.0 ; lv2:minimum 0.0 ;
lv2:maximum 100.0 ; lv2:maximum 100.0 ;
units:unit units:pc; units:unit units:pc;
rdfs:comment "Attenuation of the first delay"; rdfs:comment "Attenuation of the first echo";
], [ ], [
a lv2:InputPort , a lv2:InputPort ,
@@ -92,7 +92,7 @@ A straighforward no-frills digital delay.
lv2:minimum 0.0 ; lv2:minimum 0.0 ;
lv2:maximum 100.0 ; lv2:maximum 100.0 ;
units:unit units:pc; units:unit units:pc;
rdfs:comment "Attenuation of subequent delays"; rdfs:comment "Attenuation of subequent echoes";
], ],
[ [
a lv2:AudioPort , a lv2:AudioPort ,
Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+328
View File
@@ -0,0 +1,328 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 123.64451 57.061291"
xml:space="preserve"
sodipodi:docname="vst.svg"
width="123.64451"
height="57.061291"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"><metadata
id="metadata1016"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs1014" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1019"
id="namedview1012"
showgrid="false"
fit-margin-top="2"
fit-margin-left="2"
fit-margin-right="2"
fit-margin-bottom="2"
inkscape:zoom="7.3537604"
inkscape:cx="51.146503"
inkscape:cy="48.566934"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<style
type="text/css"
id="style849">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#030304;stroke:#FFFFFF;stroke-width:0.8877;stroke-miterlimit:10;}
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
.st2{fill:#030304;}
.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#030304;}
.st4{fill:#FFFFFF;}
</style>
<path
class="st3"
d="m 72.920741,18.998501 h 36.199999 l 0.1,6.4 h -1.7 c -0.6,-2.2 -2.2,-3.7 -4.9,-4.4 -1.5,-0.4 -3.799999,-0.7 -6.799999,-0.7 v 16 c 0,1.1 0.4,1.6 1.1,1.9 0.8,0.4 2.4,0.4 4.799999,0.4 v 1.4 H 80.420741 v -1.4 c 2.3,0 3.9,-0.2 4.7,-0.5 0.8,-0.4 1.2,-0.7 1.2,-1.8 v -16 c -2.9,0 -5.2,0.3 -6.7,0.7 -2.9,0.8 -4.5,2.3 -4.9,4.4 -0.5,0 -1.7,0 -1.7,0 z"
id="path879" />
<g
id="g885"
transform="translate(3.6207414,-0.00149918)">
<path
class="st3"
d="m 40.9,29.8 3.3,0.4 0.1,0.1 c 1.3,2.7 3.4,4.8 6.3,6 2.9,1.3 6.3,1.9 10.1,1.9 3.6,0 6.3,-0.4 7.8,-1.3 1.5,-0.8 2.2,-1.7 2.2,-2.6 0,-1 -0.8,-1.9 -2.3,-2.5 C 67.3,31.3 65.1,30.7 61.9,30 L 56.6,28.8 C 51.4,27.6 47.6,26.4 45.5,25.2 43.1,24 42,22.5 42,20.7 c 0,-1.9 1.5,-3.7 4.4,-5.4 2.9,-1.6 7.3,-2.4 13.2,-2.4 3,0 5.9,0.2 8.7,0.6 1.8,0.3 4.3,0.6 4.8,0.6 1.1,0 1.5,-0.2 1.7,-0.3 0.3,-0.2 0.6,-0.5 0.8,-0.9 l 0.1,-0.1 h 2.8 v 8.7 h -2.6 l -0.1,-0.1 c -1.1,-1.8 -3,-3.5 -5.7,-4.8 -2.7,-1.4 -6,-2 -9.8,-2 -2.7,0 -4.9,0.3 -6.4,1 -1.5,0.6 -2.2,1.4 -2.2,2.3 0,1.1 0.7,1.9 2.1,2.5 1.5,0.6 4.8,1.5 9.8,2.6 l 6.1,1.4 c 2.7,0.6 4.9,1.3 6.5,2.1 3.1,1.5 4.7,3.3 4.7,5.4 0,2 -1.7,3.9 -5,5.6 C 72.4,39.2 67.2,40 60,40 58.4,40 56.7,39.9 55,39.8 53.3,39.7 51.6,39.4 50,39.2 l -1.6,-0.3 c -0.4,0 -0.7,-0.1 -1,-0.1 -0.3,0 -0.6,-0.1 -0.9,-0.1 -0.8,0 -1.3,0.1 -1.6,0.3 -0.3,0.2 -0.5,0.5 -0.8,0.9 V 40 h -3.2 z"
id="path881" />
<path
class="st4"
d="m 78.1,13.1 v 8.2 h -2.3 c -1.1,-1.9 -3,-3.5 -5.7,-4.9 -2.7,-1.4 -6,-2.1 -9.9,-2.1 -2.8,0 -4.9,0.3 -6.5,1 -1.6,0.7 -2.4,1.5 -2.4,2.5 0,1.2 0.8,2.1 2.3,2.7 1.5,0.6 4.8,1.5 9.9,2.6 l 6.1,1.4 c 2.7,0.6 4.9,1.3 6.5,2.1 3,1.4 4.5,3.2 4.5,5.2 0,1.9 -1.6,3.7 -4.9,5.4 -3.2,1.7 -8.4,2.5 -15.5,2.5 -1.6,0 -3.3,-0.1 -5,-0.2 -1.7,-0.1 -3.3,-0.3 -5,-0.6 l -1.6,-0.3 c -0.4,0 -0.7,-0.1 -1.1,-0.1 -0.3,0 -0.6,-0.1 -0.9,-0.1 -0.8,0 -1.4,0.1 -1.7,0.3 -0.3,0.2 -0.6,0.5 -0.8,1 H 41.2 V 30 l 2.9,0.4 c 1.3,2.8 3.4,4.9 6.4,6.1 3,1.3 6.3,1.9 10.1,1.9 3.7,0 6.3,-0.4 7.9,-1.3 1.6,-0.9 2.3,-1.8 2.3,-2.8 0,-1.1 -0.8,-2 -2.5,-2.8 C 67.2,31 65,30.4 61.8,29.7 l -5.3,-1.2 c -5.2,-1.2 -8.9,-2.4 -11.1,-3.6 -2.1,-1.2 -3.2,-2.6 -3.2,-4.4 0,-1.8 1.4,-3.6 4.2,-5.2 2.9,-1.6 7.2,-2.4 13.1,-2.4 3,0 5.9,0.2 8.6,0.6 2.8,0.4 4.4,0.6 4.8,0.6 0.9,0 1.5,-0.1 1.9,-0.3 0.3,-0.2 0.6,-0.6 0.8,-1 h 2.5 m 0.4,-0.2 H 78 75.6 75.3 l -0.1,0.2 c -0.2,0.3 -0.4,0.6 -0.7,0.8 -0.1,0.1 -0.5,0.3 -1.6,0.3 -0.5,0 -3.7,-0.5 -4.7,-0.6 -2.8,-0.4 -5.7,-0.6 -8.7,-0.6 -5.9,0 -10.4,0.8 -13.3,2.5 -3,1.7 -4.5,3.5 -4.5,5.6 0,1.9 1.2,3.5 3.5,4.8 2.2,1.2 5.9,2.4 11.2,3.6 l 5.3,1.2 c 3.2,0.7 5.4,1.3 6.5,1.8 1.4,0.6 2.2,1.4 2.2,2.3 0,0.8 -0.7,1.6 -2.1,2.4 -1.5,0.8 -4.1,1.2 -7.7,1.2 -3.7,0 -7.1,-0.6 -10,-1.9 C 47.8,35 45.7,33 44.5,30.3 L 44.3,30 H 44 l -2.9,-0.4 -0.5,-0.1 v 0.5 9.7 0.5 h 0.5 2.9 0.3 L 44.4,40 c 0.2,-0.4 0.4,-0.6 0.7,-0.8 0.1,-0.1 0.5,-0.3 1.5,-0.3 0.2,0 0.5,0 0.8,0.1 0.3,0 0.7,0.1 1.1,0.1 l 1.6,0.3 c 1.6,0.3 3.3,0.5 5,0.6 1.7,0.1 3.4,0.2 5,0.2 7.2,0 12.5,-0.9 15.8,-2.6 3.4,-1.8 5.1,-3.7 5.1,-5.8 0,-2.2 -1.6,-4.1 -4.8,-5.6 -1.6,-0.8 -3.9,-1.5 -6.6,-2.1 l -6.1,-1.4 c -5,-1.1 -8.3,-2 -9.8,-2.6 -1.3,-0.5 -2,-1.3 -2,-2.2 0,-0.8 0.7,-1.5 2.1,-2.1 1.5,-0.7 3.7,-1 6.4,-1 3.8,0 7.1,0.7 9.7,2 2.7,1.3 4.5,2.9 5.6,4.7 l 0.1,0.2 h 0.3 2.3 0.5 V 21.2 13 12.6 Z"
id="path883" />
</g>
<g
id="g893"
transform="translate(3.6207414,-0.00149918)">
<g
id="g891">
<path
class="st3"
d="M 28.3,40.6 13.2,22.9 C 12.6,22.2 12,21.7 11.4,21.4 10.8,21.1 9.1,20.9 7.6,20.9 H 7.4 v -1.4 h 21 V 21 h -0.2 c -2.1,0.1 -3.4,0.1 -4.1,0.3 -0.7,0.1 -0.7,0.4 -0.7,0.7 0,0.1 0,0.3 0,0.4 0,0.1 0.1,0.2 0.2,0.3 l 8.8,10.8 h 0.1 l 7.6,-9.9 c 0.2,-0.2 0.3,-0.4 0.4,-0.6 0.3,-0.4 0.3,-0.7 0.3,-0.8 0,-0.3 -0.1,-0.7 -0.8,-0.8 -0.6,-0.1 -2,-0.2 -4,-0.3 H 35.8 V 19.6 H 49.6 V 21 h -0.2 c -1.5,0.1 -3.4,0.3 -4,0.6 -0.7,0.3 -1.3,0.9 -2,1.7 L 30.1,40.6 Z"
id="path887" />
<path
class="st4"
d="m 49.5,19.7 v 1 c -1.4,0.1 -3.4,0.3 -4.1,0.6 -0.7,0.3 -1.4,0.9 -2.1,1.8 L 30,40.4 H 28.4 L 13.3,22.7 C 12.7,22 12.1,21.5 11.4,21.2 10.8,20.9 8.9,20.7 7.5,20.6 v -1 H 28 v 1 c -2.1,0.1 -3.5,0.2 -4.1,0.3 -0.6,0.1 -0.9,0.4 -0.9,0.9 0,0.1 0,0.3 0.1,0.4 0.1,0.1 0.2,0.3 0.3,0.4 l 8.9,10.9 h 0.3 l 7.7,-9.9 c 0.2,-0.2 0.3,-0.4 0.5,-0.7 0.2,-0.4 0.3,-0.7 0.3,-0.9 0,-0.6 -0.3,-0.9 -1,-1 -0.6,-0.1 -2,-0.3 -4,-0.3 v -1 H 49.5 M 50,19.2 H 49.5 36.2 35.7 v 0.5 1 0.5 h 0.5 c 2,0.1 3.3,0.2 3.9,0.3 0.6,0.1 0.6,0.4 0.6,0.6 0,0.1 0,0.3 -0.3,0.7 -0.1,0.2 -0.3,0.4 -0.4,0.6 L 32.5,33 23.8,22.4 c -0.1,-0.1 -0.2,-0.2 -0.2,-0.3 0,-0.1 0,-0.2 0,-0.3 0,-0.1 0,-0.3 0.5,-0.4 0.6,-0.1 1.9,-0.2 4,-0.3 h 0.5 v -0.5 -1 -0.5 H 28.1 7.6 7.1 v 0.5 1 0.4 h 0.4 c 1.7,0.1 3.2,0.3 3.7,0.5 0.6,0.3 1.2,0.8 1.7,1.4 l 15.1,17.6 0.1,0.2 h 0.2 1.7 0.2 l 0.1,-0.2 13.4,-17.3 c 0.6,-0.8 1.3,-1.3 1.9,-1.6 0.5,-0.3 2.1,-0.5 3.9,-0.6 h 0.4 v -0.4 -1 -0.4 z"
id="path889" />
</g>
</g>
<g
id="g1009"
transform="translate(3.6207414,-0.00149918)">
<path
class="st4"
d="m 14.1,87.1 h -0.5 l -1.2,-7.3 h 0.5 l 1,6.3 1.1,-6.3 h 0.5 z"
id="path895" />
<path
class="st4"
d="m 16.8,84.7 v 0.6 c 0,0.6 0,0.9 0.2,1.1 0.2,0.3 0.6,0.4 0.9,0.4 0.7,0 0.9,-0.5 0.9,-1.2 0,-0.8 -0.2,-1.1 -1.1,-2 -0.8,-0.8 -1.3,-1.3 -1.3,-2.3 0,-0.6 0.1,-1.6 1.4,-1.6 1.3,0 1.4,1 1.4,1.7 v 0.5 h -0.5 v -0.7 c 0,-0.4 0,-1.1 -0.9,-1.1 -0.6,0 -0.9,0.4 -0.9,1.1 0,0.7 0.1,1 1.2,2.1 0.8,0.8 1.1,1.2 1.1,2.1 0,0.7 -0.1,1.7 -1.5,1.7 -0.9,0 -1.5,-0.4 -1.5,-1.5 v -1 h 0.6 z"
id="path897" />
<path
class="st4"
d="m 21.7,80.2 v 6.9 H 21.2 V 80.2 H 19.9 V 79.7 H 23 v 0.5 z"
id="path899" />
<path
class="st4"
d="m 25.8,81.3 v -0.8 h 0.4 v 0.8 z m 0,5.8 v -5.2 h 0.4 v 5.2 z"
id="path901" />
<path
class="st4"
d="m 27.6,85.3 v 0.5 c 0,0.3 0,1 0.8,1 0.4,0 0.8,-0.2 0.8,-0.9 0,-0.5 -0.1,-0.7 -0.9,-1.3 -0.6,-0.5 -0.9,-0.8 -0.9,-1.6 0,-0.4 0.1,-1.1 1.1,-1.1 1.1,0 1.1,0.8 1.1,1.2 v 0.4 h -0.4 v -0.4 c 0,-0.6 -0.2,-0.8 -0.6,-0.8 -0.6,0 -0.7,0.4 -0.7,0.7 0,0.5 0,0.6 1,1.4 0.5,0.4 0.8,0.7 0.8,1.4 0,0.8 -0.3,1.3 -1.2,1.3 -1.1,0 -1.2,-0.6 -1.2,-1.5 v -0.4 h 0.3 z"
id="path903" />
<path
class="st4"
d="m 35.1,87.1 c -0.4,0 -0.6,-0.1 -0.7,-0.4 -0.2,0.1 -0.5,0.4 -1,0.4 -0.7,0 -1,-0.6 -1,-1.2 0,-1.5 1.4,-1.6 2,-2.2 V 83 c 0,-0.4 -0.1,-0.7 -0.6,-0.7 -0.5,0 -0.7,0.3 -0.7,1 v 0.3 h -0.4 v -0.3 c 0,-1 0.4,-1.4 1.2,-1.4 0.7,0 1.1,0.4 1.1,1.1 v 3.6 c 0,0.2 0.1,0.2 0.3,0.2 z m -0.7,-2.9 c -0.3,0.2 -0.6,0.3 -0.9,0.5 -0.5,0.3 -0.7,0.6 -0.7,1.2 0,0.2 0,0.8 0.7,0.8 0.4,0 0.7,-0.2 0.9,-0.5 z"
id="path905" />
<path
class="st4"
d="m 38.5,81.9 v 0.9 c 0.2,-0.6 0.7,-0.9 1.1,-1.1 v 0.6 c -0.1,0 -0.3,0.1 -0.4,0.2 -0.4,0.2 -0.6,0.8 -0.7,0.9 v 3.7 H 38 v -5.2 z"
id="path907" />
<path
class="st4"
d="m 42.6,85.9 c 0,0.6 -0.3,1.2 -1.2,1.2 -0.7,0 -1.2,-0.4 -1.2,-1.2 v -2.8 c 0,-0.6 0.3,-1.2 1.2,-1.2 0.8,0 1.2,0.4 1.2,1.4 v 1.3 h -2 v 1.3 c 0,0.3 0.1,0.9 0.7,0.9 0.7,0 0.8,-0.6 0.8,-1.1 v -0.4 h 0.4 v 0.6 z m -0.5,-2.8 c 0,-0.6 -0.3,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 v 1 h 1.5 z"
id="path909" />
<path
class="st4"
d="m 46,87.5 c 0,0.9 -0.6,1.3 -1.3,1.3 -0.4,0 -0.9,-0.1 -1,-0.5 C 43.6,88.2 43.6,88 43.6,87.7 H 44 c 0,0 0,0.2 0.1,0.4 0.2,0.3 0.6,0.3 0.6,0.3 0.1,0 0.8,-0.1 0.8,-0.8 v -0.9 c -0.1,0.1 -0.4,0.5 -0.9,0.5 -0.7,0 -1,-0.6 -1,-1.2 v -3 c 0,-0.9 0.5,-1.2 1.1,-1.2 0.5,0 0.8,0.3 0.9,0.5 V 81.9 H 46 Z M 45.6,83 c -0.1,-0.4 -0.4,-0.7 -0.8,-0.7 -0.6,0 -0.7,0.5 -0.7,0.9 v 2.6 c 0,0.7 0.3,0.9 0.7,0.9 0.3,0 0.7,-0.2 0.8,-0.8 z"
id="path911" />
<path
class="st4"
d="m 47.2,81.3 v -0.8 h 0.4 v 0.8 z m 0,5.8 v -5.2 h 0.4 v 5.2 z"
id="path913" />
<path
class="st4"
d="m 49,85.3 v 0.5 c 0,0.3 0,1 0.8,1 0.4,0 0.8,-0.2 0.8,-0.9 0,-0.5 -0.1,-0.7 -0.9,-1.3 -0.6,-0.5 -0.9,-0.8 -0.9,-1.6 0,-0.4 0.1,-1.1 1.1,-1.1 1.1,0 1.1,0.8 1.1,1.2 v 0.4 h -0.4 v -0.4 c 0,-0.6 -0.2,-0.8 -0.6,-0.8 -0.6,0 -0.7,0.4 -0.7,0.7 0,0.5 0,0.6 1,1.4 0.5,0.4 0.8,0.7 0.8,1.4 0,0.8 -0.3,1.3 -1.2,1.3 -1.1,0 -1.2,-0.6 -1.2,-1.5 V 85.2 H 49 Z"
id="path915" />
<path
class="st4"
d="m 51.5,81.9 h 0.6 v -1.4 h 0.4 v 1.4 h 0.8 v 0.4 h -0.8 v 4.1 c 0,0.3 0.3,0.3 0.4,0.3 0.1,0 0.3,0 0.4,0 v 0.4 c -0.2,0 -0.3,0 -0.5,0 -0.6,0 -0.8,-0.2 -0.8,-0.6 v -4.2 h -0.6 v -0.4 z"
id="path917" />
<path
class="st4"
d="m 56.3,85.9 c 0,0.6 -0.3,1.2 -1.2,1.2 -0.7,0 -1.2,-0.4 -1.2,-1.2 v -2.8 c 0,-0.6 0.3,-1.2 1.2,-1.2 0.8,0 1.2,0.4 1.2,1.4 v 1.3 h -2 v 1.3 c 0,0.3 0.1,0.9 0.7,0.9 0.7,0 0.8,-0.6 0.8,-1.1 v -0.4 h 0.4 v 0.6 z m -0.4,-2.8 c 0,-0.6 -0.3,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 v 1 h 1.5 z"
id="path919" />
<path
class="st4"
d="m 57.8,81.9 v 0.9 c 0.2,-0.6 0.7,-0.9 1.1,-1.1 v 0.6 c -0.1,0 -0.3,0.1 -0.4,0.2 -0.4,0.2 -0.6,0.8 -0.7,0.9 v 3.7 h -0.4 v -5.2 z"
id="path921" />
<path
class="st4"
d="m 62,85.9 c 0,0.6 -0.3,1.2 -1.2,1.2 -0.7,0 -1.2,-0.4 -1.2,-1.2 v -2.8 c 0,-0.6 0.3,-1.2 1.2,-1.2 0.8,0 1.2,0.4 1.2,1.4 v 1.3 h -2 v 1.3 c 0,0.3 0.1,0.9 0.7,0.9 0.7,0 0.8,-0.6 0.8,-1.1 V 85.3 H 62 Z m -0.5,-2.8 c 0,-0.6 -0.3,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 v 1 h 1.5 z"
id="path923" />
<path
class="st4"
d="m 65,87.1 v -0.6 c -0.1,0.2 -0.4,0.6 -1,0.6 -0.5,0 -1,-0.3 -1,-1.2 v -2.8 c 0,-0.9 0.5,-1.3 1.1,-1.3 0.6,0 0.8,0.4 0.9,0.5 v -2.6 h 0.4 V 87 H 65 Z m 0,-3.9 c -0.1,-0.5 -0.3,-0.9 -0.8,-0.9 -0.5,0 -0.7,0.4 -0.7,0.8 v 2.7 c 0,0.3 0.1,0.9 0.7,0.9 0.4,0 0.7,-0.4 0.8,-0.8 z"
id="path925" />
<path
class="st4"
d="m 68.2,81.9 h 0.6 v -1.4 h 0.4 v 1.4 H 70 v 0.4 h -0.8 v 4.1 c 0,0.3 0.3,0.3 0.4,0.3 0.1,0 0.3,0 0.4,0 v 0.4 c -0.2,0 -0.3,0 -0.5,0 -0.6,0 -0.8,-0.2 -0.8,-0.6 v -4.2 h -0.6 v -0.4 z"
id="path927" />
<path
class="st4"
d="m 71.1,81.9 v 0.9 c 0.2,-0.6 0.7,-0.9 1.1,-1.1 v 0.6 c -0.1,0 -0.3,0.1 -0.4,0.2 -0.4,0.2 -0.6,0.8 -0.7,0.9 v 3.7 h -0.4 v -5.2 z"
id="path929" />
<path
class="st4"
d="m 75.4,87.1 c -0.4,0 -0.6,-0.1 -0.7,-0.4 -0.2,0.1 -0.5,0.4 -1,0.4 -0.7,0 -1,-0.6 -1,-1.2 0,-1.5 1.4,-1.6 2,-2.2 V 83 c 0,-0.4 -0.1,-0.7 -0.6,-0.7 -0.5,0 -0.7,0.3 -0.7,1 v 0.3 H 73 v -0.3 c 0,-1 0.4,-1.4 1.2,-1.4 0.8,0 1.1,0.4 1.1,1.1 v 3.6 c 0,0.2 0.1,0.2 0.3,0.2 z m -0.7,-2.9 c -0.3,0.2 -0.6,0.3 -0.9,0.5 -0.5,0.3 -0.7,0.6 -0.7,1.2 0,0.2 0,0.8 0.7,0.8 0.4,0 0.7,-0.2 0.9,-0.5 z"
id="path931" />
<path
class="st4"
d="m 78.3,87.1 v -0.6 c -0.1,0.2 -0.4,0.6 -1,0.6 -0.5,0 -1,-0.3 -1,-1.2 v -2.8 c 0,-0.9 0.5,-1.3 1.1,-1.3 0.6,0 0.8,0.4 0.9,0.5 v -2.6 h 0.4 V 87 h -0.4 z m -0.1,-3.9 c -0.1,-0.5 -0.3,-0.9 -0.8,-0.9 -0.5,0 -0.7,0.4 -0.7,0.8 v 2.7 c 0,0.3 0.1,0.9 0.7,0.9 0.4,0 0.8,-0.4 0.8,-0.8 z"
id="path933" />
<path
class="st4"
d="m 82.2,85.9 c 0,0.6 -0.3,1.2 -1.2,1.2 -0.7,0 -1.2,-0.4 -1.2,-1.2 v -2.8 c 0,-0.6 0.3,-1.2 1.2,-1.2 0.8,0 1.2,0.4 1.2,1.4 v 1.3 h -2 v 1.3 c 0,0.3 0.1,0.9 0.7,0.9 0.7,0 0.8,-0.6 0.8,-1.1 v -0.4 h 0.4 v 0.6 z m -0.5,-2.8 c 0,-0.6 -0.3,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 v 1 h 1.5 z"
id="path935" />
<path
class="st4"
d="m 87.2,87.1 v -4 c 0,-0.5 -0.1,-0.9 -0.7,-0.9 -0.4,0 -0.7,0.3 -0.9,0.6 V 87 h -0.4 v -4 c 0,-0.5 -0.2,-0.7 -0.7,-0.7 -0.5,0 -0.8,0.4 -0.8,0.6 v 4.3 H 83.3 V 82 h 0.4 v 0.4 c 0.1,-0.2 0.5,-0.5 1,-0.5 0.6,0 0.8,0.4 0.9,0.6 0.1,-0.2 0.4,-0.6 1.1,-0.6 0.5,0 1,0.3 1,1.2 v 4.2 h -0.5 z"
id="path937" />
<path
class="st4"
d="m 91.2,87.1 c -0.4,0 -0.6,-0.1 -0.7,-0.4 -0.2,0.1 -0.5,0.4 -1,0.4 -0.7,0 -1,-0.6 -1,-1.2 0,-1.5 1.4,-1.6 2,-2.2 V 83 c 0,-0.4 -0.1,-0.7 -0.6,-0.7 -0.5,0 -0.7,0.3 -0.7,1 v 0.3 h -0.4 v -0.3 c 0,-1 0.4,-1.4 1.2,-1.4 0.7,0 1.1,0.4 1.1,1.1 v 3.6 c 0,0.2 0.1,0.2 0.3,0.2 z m -0.6,-2.9 c -0.3,0.2 -0.6,0.3 -0.9,0.5 -0.5,0.3 -0.7,0.6 -0.7,1.2 0,0.2 0,0.8 0.7,0.8 0.4,0 0.7,-0.2 0.9,-0.5 z"
id="path939" />
<path
class="st4"
d="m 92.6,81.9 v 0.9 c 0.2,-0.6 0.7,-0.9 1.1,-1.1 v 0.6 c -0.1,0 -0.3,0.1 -0.4,0.2 -0.4,0.2 -0.6,0.8 -0.7,0.9 v 3.7 h -0.4 v -5.2 z"
id="path941" />
<path
class="st4"
d="m 96.5,87.1 -1.1,-2.8 -0.5,0.8 V 87 h -0.4 v -7.3 h 0.4 v 4.6 l 1.5,-2.5 h 0.5 l -1.2,2 1.3,3.2 h -0.5 z"
id="path943" />
<path
class="st4"
d="m 102.1,85.7 c 0,1 -0.6,1.4 -1.2,1.4 -0.7,0 -1.2,-0.4 -1.2,-1.1 v -2.9 c 0,-0.6 0.3,-1.3 1.3,-1.3 0.8,0 1.2,0.5 1.2,1.3 v 2.6 z m -0.4,-2.5 c 0,-0.4 -0.1,-0.9 -0.7,-0.9 -0.7,0 -0.8,0.5 -0.8,1 v 2.6 c 0,0.6 0.3,0.8 0.8,0.8 0.7,0 0.8,-0.6 0.8,-0.9 v -2.6 z"
id="path945" />
<path
class="st4"
d="m 102.8,81.9 h 0.6 v -1.3 c 0,-0.4 0.2,-0.8 0.9,-0.8 0.1,0 0.2,0 0.3,0 v 0.4 c 0,0 -0.1,0 -0.2,0 -0.3,0 -0.6,0.1 -0.6,0.6 v 1.1 h 0.6 v 0.4 h -0.7 v 4.8 h -0.4 v -4.8 h -0.6 v -0.4 z"
id="path947" />
<path
class="st4"
d="m 6.9,96.7 v 0.6 c 0,0.6 0,0.9 0.2,1.1 0.2,0.3 0.6,0.4 0.9,0.4 0.7,0 0.9,-0.5 0.9,-1.2 0,-0.8 -0.2,-1.1 -1.1,-2 C 7,94.8 6.5,94.3 6.5,93.3 c 0,-0.6 0.1,-1.6 1.4,-1.6 1.3,0 1.4,1 1.4,1.7 v 0.5 H 8.8 v -0.7 c 0,-0.4 0,-1.1 -0.9,-1.1 -0.6,0 -0.9,0.4 -0.9,1.1 0,0.7 0.1,1 1.2,2.1 0.8,0.8 1.1,1.2 1.1,2.1 0,0.7 -0.1,1.7 -1.5,1.7 -0.9,0 -1.5,-0.4 -1.5,-1.5 v -1 h 0.6 z"
id="path949" />
<path
class="st4"
d="m 9.9,93.9 h 0.6 v -1.4 h 0.4 v 1.4 h 0.8 v 0.4 h -0.8 v 4.1 c 0,0.3 0.3,0.3 0.4,0.3 0.1,0 0.3,0 0.4,0 v 0.4 c -0.2,0 -0.3,0 -0.5,0 -0.6,0 -0.8,-0.2 -0.8,-0.6 V 94.3 H 9.9 Z"
id="path951" />
<path
class="st4"
d="m 14.7,98 c 0,0.6 -0.3,1.2 -1.2,1.2 -0.7,0 -1.2,-0.4 -1.2,-1.2 v -2.8 c 0,-0.6 0.3,-1.2 1.2,-1.2 0.8,0 1.2,0.4 1.2,1.4 v 1.3 h -2 V 98 c 0,0.3 0.1,0.9 0.7,0.9 0.7,0 0.8,-0.6 0.8,-1.1 v -0.4 h 0.4 V 98 Z m -0.5,-2.8 c 0,-0.6 -0.3,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 v 1 h 1.5 z"
id="path953" />
<path
class="st4"
d="m 15.7,93.3 v -0.8 h 0.4 v 0.8 z m 0,5.9 V 94 h 0.4 v 5.2 z"
id="path955" />
<path
class="st4"
d="M 19.3,99.2 V 95 c 0,-0.4 -0.1,-0.8 -0.6,-0.8 -0.4,0 -0.7,0.2 -0.9,0.7 v 4.3 H 17.4 V 94 h 0.4 v 0.5 c 0.3,-0.5 0.8,-0.6 1,-0.6 0.4,0 1,0.2 1,1.1 v 4.2 z"
id="path957" />
<path
class="st4"
d="m 20.9,91.8 h 0.4 v 2.6 c 0.3,-0.4 0.7,-0.5 1,-0.5 0.9,0 1,0.8 1,1.3 v 2.7 c 0,0.5 -0.3,1.2 -1.1,1.2 -0.1,0 -0.6,0 -0.8,-0.5 V 99 h -0.5 z m 0.5,5.9 c 0,0.9 0.6,1 0.8,1 0.5,0 0.7,-0.4 0.7,-1 v -2.6 c 0,-0.2 0,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 z"
id="path959" />
<path
class="st4"
d="m 26.8,98 c 0,0.6 -0.3,1.2 -1.2,1.2 -0.7,0 -1.2,-0.4 -1.2,-1.2 v -2.8 c 0,-0.6 0.3,-1.2 1.2,-1.2 0.8,0 1.2,0.4 1.2,1.4 v 1.3 h -2 V 98 c 0,0.3 0.1,0.9 0.7,0.9 0.7,0 0.8,-0.6 0.8,-1.1 v -0.4 h 0.4 V 98 Z m -0.5,-2.8 c 0,-0.6 -0.3,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 v 1 h 1.5 z"
id="path961" />
<path
class="st4"
d="m 28.3,93.9 v 0.9 c 0.2,-0.6 0.7,-0.9 1.1,-1.1 v 0.6 c -0.1,0 -0.3,0.1 -0.4,0.2 -0.4,0.2 -0.6,0.8 -0.7,0.9 v 3.7 h -0.4 v -5.2 z"
id="path963" />
<path
class="st4"
d="m 32.5,99.5 c 0,0.9 -0.6,1.3 -1.3,1.3 -0.4,0 -0.9,-0.1 -1,-0.5 -0.1,-0.1 -0.1,-0.3 -0.1,-0.6 h 0.4 c 0,0 0,0.2 0.1,0.4 0.2,0.3 0.6,0.3 0.6,0.3 0.1,0 0.8,-0.1 0.8,-0.8 v -0.9 c -0.1,0.1 -0.4,0.5 -0.9,0.5 -0.7,0 -1,-0.6 -1,-1.2 v -2.9 c 0,-0.9 0.5,-1.2 1.1,-1.2 0.5,0 0.8,0.3 0.9,0.5 V 94 h 0.4 z M 32,95 c -0.1,-0.4 -0.4,-0.7 -0.8,-0.7 -0.6,0 -0.7,0.5 -0.7,0.9 v 2.6 c 0,0.7 0.3,0.9 0.7,0.9 0.3,0 0.7,-0.2 0.8,-0.8 z"
id="path965" />
<path
class="st4"
d="m 39.3,99.2 c 0,-0.5 0,-5.1 0,-6.8 L 38,99.2 h -0.5 l -1.3,-6.8 v 6.8 h -0.5 c 0,-0.5 0.1,-7.3 0.1,-7.3 h 0.7 l 1.2,6.3 1.2,-6.3 h 0.7 c 0,0.8 0.1,7.3 0.1,7.3 z"
id="path967" />
<path
class="st4"
d="m 43.3,98 c 0,0.6 -0.3,1.2 -1.2,1.2 -0.7,0 -1.2,-0.4 -1.2,-1.2 v -2.8 c 0,-0.6 0.3,-1.2 1.2,-1.2 0.8,0 1.2,0.4 1.2,1.4 v 1.3 h -2 V 98 c 0,0.3 0.1,0.9 0.7,0.9 0.7,0 0.8,-0.6 0.8,-1.1 v -0.4 h 0.4 V 98 Z m -0.4,-2.8 c 0,-0.6 -0.3,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 v 1 h 1.5 z"
id="path969" />
<path
class="st4"
d="m 46.3,99.2 v -0.6 c -0.1,0.2 -0.4,0.6 -1,0.6 -0.5,0 -1,-0.3 -1,-1.2 v -2.8 c 0,-0.9 0.5,-1.3 1.1,-1.3 0.6,0 0.8,0.4 0.9,0.5 v -2.6 h 0.4 v 7.3 h -0.4 z m 0,-3.9 c -0.1,-0.5 -0.3,-0.9 -0.8,-0.9 -0.5,0 -0.7,0.4 -0.7,0.8 v 2.7 c 0,0.3 0.1,0.9 0.7,0.9 0.4,0 0.7,-0.4 0.8,-0.8 z"
id="path971" />
<path
class="st4"
d="m 48,93.3 v -0.8 h 0.4 v 0.8 z m 0,5.9 V 94 h 0.4 v 5.2 z"
id="path973" />
<path
class="st4"
d="m 52,99.2 c -0.4,0 -0.6,-0.1 -0.7,-0.4 -0.2,0.1 -0.5,0.4 -1,0.4 -0.7,0 -1,-0.6 -1,-1.2 0,-1.5 1.4,-1.6 2,-2.2 V 95 c 0,-0.4 -0.1,-0.7 -0.6,-0.7 -0.5,0 -0.7,0.3 -0.7,1 v 0.3 h -0.4 v -0.3 c 0,-1 0.4,-1.4 1.2,-1.4 0.7,0 1.1,0.4 1.1,1.1 v 3.6 c 0,0.2 0.1,0.2 0.3,0.2 z m -0.6,-3 c -0.3,0.2 -0.6,0.3 -0.9,0.5 -0.5,0.3 -0.7,0.6 -0.7,1.2 0,0.2 0,0.8 0.7,0.8 0.4,0 0.7,-0.2 0.9,-0.5 z"
id="path975" />
<path
class="st4"
d="m 56.3,92.3 v 6.9 h -0.5 v -6.9 h -1.3 v -0.5 h 3.1 v 0.5 z"
id="path977" />
<path
class="st4"
d="m 60,98 c 0,0.6 -0.3,1.2 -1.2,1.2 -0.7,0 -1.2,-0.4 -1.2,-1.2 v -2.8 c 0,-0.6 0.3,-1.2 1.2,-1.2 0.8,0 1.2,0.4 1.2,1.4 v 1.3 H 58 V 98 c 0,0.3 0.1,0.9 0.7,0.9 0.7,0 0.8,-0.6 0.8,-1.1 V 97.4 H 60 Z m -0.4,-2.8 c 0,-0.6 -0.3,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 v 1 h 1.5 z"
id="path979" />
<path
class="st4"
d="m 63.5,97.8 c 0,0.8 -0.3,1.4 -1.3,1.4 -0.9,0 -1.2,-0.7 -1.2,-1.3 v -2.6 c 0,-1 0.5,-1.3 1.2,-1.3 0.8,0 1.2,0.5 1.2,1.4 v 0.5 H 63 v -0.4 c 0,-0.5 -0.1,-1.1 -0.8,-1.1 -0.7,0 -0.8,0.6 -0.8,1 v 2.3 c 0,0.5 0,1.2 0.8,1.2 0.6,0 0.7,-0.5 0.7,-0.9 v -0.8 h 0.5 v 0.6 z"
id="path981" />
<path
class="st4"
d="m 66.4,99.2 v -3.9 c 0,-0.5 -0.1,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.7 v 4.2 H 64.5 V 92 h 0.4 v 2.5 C 65,94.4 65.3,94 65.8,94 c 0.4,0 1,0.2 1,1.1 v 4.2 h -0.4 z"
id="path983" />
<path
class="st4"
d="M 70,99.2 V 95 c 0,-0.4 -0.1,-0.8 -0.6,-0.8 -0.4,0 -0.7,0.2 -0.9,0.7 v 4.3 H 68 V 94 h 0.4 v 0.5 c 0.3,-0.5 0.8,-0.6 1,-0.6 0.4,0 1,0.2 1,1.1 v 4.2 z"
id="path985" />
<path
class="st4"
d="m 74,97.8 c 0,1 -0.6,1.4 -1.2,1.4 -0.7,0 -1.2,-0.4 -1.2,-1.1 v -2.9 c 0,-0.6 0.3,-1.3 1.3,-1.3 0.8,0 1.2,0.5 1.2,1.3 v 2.6 z m -0.5,-2.6 c 0,-0.4 -0.1,-0.9 -0.7,-0.9 -0.7,0 -0.8,0.5 -0.8,1 v 2.6 c 0,0.6 0.3,0.8 0.8,0.8 0.7,0 0.8,-0.6 0.8,-0.9 v -2.6 z"
id="path987" />
<path
class="st4"
d="m 75,99.2 v -7.3 h 0.4 v 7.3 z"
id="path989" />
<path
class="st4"
d="m 79.1,97.8 c 0,1 -0.6,1.4 -1.2,1.4 -0.7,0 -1.2,-0.4 -1.2,-1.1 v -2.9 c 0,-0.6 0.3,-1.3 1.3,-1.3 0.8,0 1.2,0.5 1.2,1.3 v 2.6 z m -0.5,-2.6 c 0,-0.4 -0.1,-0.9 -0.7,-0.9 -0.7,0 -0.8,0.5 -0.8,1 v 2.6 c 0,0.6 0.3,0.8 0.8,0.8 0.7,0 0.8,-0.6 0.8,-0.9 v -2.6 z"
id="path991" />
<path
class="st4"
d="m 82.6,99.5 c 0,0.9 -0.6,1.3 -1.3,1.3 -0.4,0 -0.9,-0.1 -1,-0.5 -0.1,-0.1 -0.1,-0.3 -0.1,-0.6 h 0.4 c 0,0 0,0.2 0.1,0.4 0.2,0.3 0.6,0.3 0.6,0.3 0.1,0 0.8,-0.1 0.8,-0.8 v -0.9 c -0.1,0.1 -0.4,0.5 -0.9,0.5 -0.8,0 -1,-0.6 -1,-1.2 v -2.9 c 0,-0.9 0.5,-1.2 1.1,-1.2 0.5,0 0.8,0.3 0.9,0.5 V 94 h 0.4 z M 82.2,95 c -0.1,-0.4 -0.4,-0.7 -0.8,-0.7 -0.6,0 -0.7,0.5 -0.7,0.9 v 2.6 c 0,0.7 0.3,0.9 0.7,0.9 0.3,0 0.7,-0.2 0.8,-0.8 z"
id="path993" />
<path
class="st4"
d="m 83.8,93.3 v -0.8 h 0.4 v 0.8 z m 0,5.9 V 94 h 0.4 v 5.2 z"
id="path995" />
<path
class="st4"
d="m 87.7,98 c 0,0.6 -0.3,1.2 -1.2,1.2 -0.7,0 -1.2,-0.4 -1.2,-1.2 v -2.8 c 0,-0.6 0.3,-1.2 1.2,-1.2 0.8,0 1.2,0.4 1.2,1.4 v 1.3 h -2 V 98 c 0,0.3 0.1,0.9 0.7,0.9 0.7,0 0.8,-0.6 0.8,-1.1 v -0.4 h 0.4 V 98 Z m -0.4,-2.8 c 0,-0.6 -0.3,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 v 1 h 1.5 z"
id="path997" />
<path
class="st4"
d="m 89,97.3 v 0.5 c 0,0.3 0,1 0.8,1 0.4,0 0.8,-0.2 0.8,-0.9 0,-0.5 -0.1,-0.7 -0.9,-1.3 -0.6,-0.5 -0.9,-0.8 -0.9,-1.6 0,-0.4 0.1,-1.1 1.1,-1.1 1.1,0 1.1,0.8 1.1,1.2 v 0.4 h -0.4 v -0.4 c 0,-0.6 -0.2,-0.8 -0.6,-0.8 -0.6,0 -0.7,0.4 -0.7,0.7 0,0.5 0,0.6 1,1.4 0.5,0.4 0.8,0.7 0.8,1.4 0,0.8 -0.3,1.3 -1.2,1.3 -1.1,0 -1.2,-0.6 -1.2,-1.5 V 97.2 H 89 Z"
id="path999" />
<path
class="st4"
d="m 96.6,99.2 v -0.6 c -0.1,0.2 -0.5,0.7 -1.2,0.7 -0.4,0 -0.9,-0.2 -1.1,-0.6 C 94.1,98.3 94,98 94,97.3 v -3.9 c 0,-1 0.4,-1.6 1.5,-1.6 1.1,0 1.4,0.7 1.4,1.3 v 1.1 h -0.5 v -0.9 c 0,-0.6 -0.1,-1.1 -1,-1.1 -0.8,0 -1,0.6 -1,1.2 v 3.8 c 0,0.6 0,0.9 0.1,1.2 0.1,0.2 0.3,0.5 0.8,0.5 0.7,0 1,-0.8 1,-1.2 V 96.4 H 95.2 V 96 H 97 v 3.4 h -0.4 z"
id="path1001" />
<path
class="st4"
d="m 102.1,99.2 v -4 c 0,-0.5 -0.1,-0.9 -0.7,-0.9 -0.4,0 -0.7,0.3 -0.9,0.6 v 4.2 h -0.4 V 95 c 0,-0.5 -0.2,-0.7 -0.7,-0.7 -0.5,0 -0.8,0.4 -0.8,0.6 v 4.3 H 98.2 V 94 h 0.4 v 0.4 c 0.1,-0.2 0.5,-0.5 1,-0.5 0.6,0 0.8,0.4 0.9,0.6 0.1,-0.2 0.4,-0.6 1.1,-0.6 0.5,0 1,0.3 1,1.2 v 4.2 h -0.5 z"
id="path1003" />
<path
class="st4"
d="m 103.7,91.8 h 0.4 v 2.6 c 0.3,-0.4 0.7,-0.5 1,-0.5 0.9,0 1,0.8 1,1.3 v 2.7 c 0,0.5 -0.3,1.2 -1.1,1.2 -0.1,0 -0.6,0 -0.8,-0.5 V 99 h -0.5 z m 0.5,5.9 c 0,0.9 0.6,1 0.8,1 0.5,0 0.7,-0.4 0.7,-1 v -2.6 c 0,-0.2 0,-0.9 -0.7,-0.9 -0.5,0 -0.8,0.4 -0.8,0.9 z"
id="path1005" />
<path
class="st4"
d="m 109.6,99.2 v -3.4 h -1.8 v 3.4 h -0.5 v -7 h 0.5 v 3.1 h 1.8 v -3.1 h 0.5 v 7 z"
id="path1007" />
</g>
<rect
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:4;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect1018"
width="115.64451"
height="49.061291"
x="4"
y="4" /></svg>

After

Width:  |  Height:  |  Size: 22 KiB

+1 -1
View File
@@ -1,5 +1,5 @@
{ {
"socket_server_port": 80, "socket_server_port": 8080,
"socket_server_address": "*", "socket_server_address": "*",
"debug": true, "debug": true,
"max_upload_size": 1048576, "max_upload_size": 1048576,
+18 -2
View File
@@ -228,6 +228,7 @@ export const LoadPluginDialog =
let gridWidth = width - this.margin_reserve; let gridWidth = width - this.margin_reserve;
let columns = Math.floor((gridWidth) / this.nominal_column_width); let columns = Math.floor((gridWidth) / this.nominal_column_width);
if (columns < 1) columns = 1; if (columns < 1) columns = 1;
if (columns > 3) columns = 3;
return columns; return columns;
} }
@@ -388,6 +389,14 @@ export const LoadPluginDialog =
// let selectedUri = this.state.selected_uri; // let selectedUri = this.state.selected_uri;
} }
vst3_indicator(uiPlugin? : UiPlugin)
{
if (uiPlugin?.is_vst3)
{
return (<img alt="vst" src="/img/vst.svg" style={{marginLeft: 8,height: 22, opacity: 0.6,position: "relative",top: -1}} />)
}
return null;
}
stereo_indicator(uiPlugin?: UiPlugin): string { stereo_indicator(uiPlugin?: UiPlugin): string {
if (!uiPlugin) return ""; if (!uiPlugin) return "";
if (uiPlugin.audio_inputs === 2 || uiPlugin.audio_outputs === 2) { if (uiPlugin.audio_inputs === 2 || uiPlugin.audio_outputs === 2) {
@@ -436,7 +445,13 @@ export const LoadPluginDialog =
for (let i = 0; i < plugins.length; ++i) { for (let i = 0; i < plugins.length; ++i) {
let plugin = plugins[i]; let plugin = plugins[i];
if (filterType === PluginType.Plugin || rootClass.is_type_of(filterType, plugin.plugin_type)) { if (filterType === PluginType.Plugin || rootClass.is_type_of(filterType, plugin.plugin_type)) {
let score = searchFilter.score(plugin.name, plugin.plugin_display_type, plugin.author_name); let score:number = 0;
if (plugin.is_vst3)
{
score = searchFilter.score(plugin.name, plugin.plugin_display_type, plugin.author_name,"vst3");
} else {
score = searchFilter.score(plugin.name, plugin.plugin_display_type, plugin.author_name);
}
if (score !== 0) { if (score !== 0) {
if (this.state.favoritesList[plugin.uri]) { if (this.state.favoritesList[plugin.uri]) {
@@ -668,8 +683,9 @@ export const LoadPluginDialog =
<div style={{ display: "flex", justifyContent: "start", alignItems: "center", flex: "1 1 auto", height: "100%", overflow: "hidden" }} > <div style={{ display: "flex", justifyContent: "start", alignItems: "center", flex: "1 1 auto", height: "100%", overflow: "hidden" }} >
<PluginInfoDialog plugin_uri={this.state.selected_uri ?? ""} /> <PluginInfoDialog plugin_uri={this.state.selected_uri ?? ""} />
<Typography display='block' variant='body2' color="textPrimary" noWrap > <Typography display='block' variant='body2' color="textPrimary" noWrap >
{this.info_string(selectedPlugin)} {this.info_string(selectedPlugin)}
</Typography> </Typography>
{this.vst3_indicator(selectedPlugin)}
<div style={{ <div style={{
color: isFavorite ? "#F80" : "#CCC", display: (this.state.selected_uri !== "" ? "block" : "none"), color: isFavorite ? "#F80" : "#CCC", display: (this.state.selected_uri !== "" ? "block" : "none"),
position: "relative", top: -2 position: "relative", top: -2
+26 -2
View File
@@ -45,6 +45,8 @@ export class Port implements Deserializable<Port> {
this.supports_time_position = input.supports_time_position; this.supports_time_position = input.supports_time_position;
this.port_group = input.port_group; this.port_group = input.port_group;
this.comment = input.comment; this.comment = input.comment;
this.is_bypass = input.is_bypass;
this.is_program_controller = input.is_program_controller;
return this; return this;
} }
@@ -75,12 +77,17 @@ export class Port implements Deserializable<Port> {
supports_time_position: boolean = false; supports_time_position: boolean = false;
port_group: string = ""; port_group: string = "";
comment: string = ""; comment: string = "";
is_bypass: boolean = false;
is_program_controller: boolean = false;
} }
export class PortGroup { export class PortGroup {
deserialize(input: any): PortGroup { deserialize(input: any): PortGroup {
this.symbol = input.symbol; this.symbol = input.symbol;
this.name = input.name; this.name = input.name;
this.parent_group = input.parent_group;
this.program_list_id = input.program_list_id ?? -1;
return this; return this;
} }
static deserialize_array(input: any) : PortGroup[] { static deserialize_array(input: any) : PortGroup[] {
@@ -94,6 +101,8 @@ export class PortGroup {
symbol: string = ""; symbol: string = "";
name: string = ""; name: string = "";
parent_group: string = "";
program_list_id: number = -1;
}; };
export class Lv2Plugin implements Deserializable<Lv2Plugin> { export class Lv2Plugin implements Deserializable<Lv2Plugin> {
@@ -223,10 +232,19 @@ export class UiControl implements Deserializable<UiControl> {
this.scale_points = ScalePoint.deserialize_array(input.scale_points); this.scale_points = ScalePoint.deserialize_array(input.scale_points);
this.port_group = input.port_group; this.port_group = input.port_group;
this.units = input.units as Units; this.units = input.units as Units;
this.comment = input.comment;
this.comment = input.comment ?? "";
this.is_bypass = input.is_bypass ? true: false;
this.is_program_controller = input.is_program_controller? true: false;
this.custom_units = input.custom_units ?? "";
this.controlType = ControlType.Dial; this.controlType = ControlType.Dial;
if (this.is_bypass)
{
this.not_on_gui = true;
}
if (this.enumeration_property && this.scale_points.length === 2) if (this.enumeration_property && this.scale_points.length === 2)
{ {
@@ -234,7 +252,7 @@ export class UiControl implements Deserializable<UiControl> {
} else { } else {
if (this.min_value === 0 && this.max_value === 1) if (this.min_value === 0 && this.max_value === 1)
{ {
if (this.toggled_property || this.integer_property) if (this.toggled_property || this.integer_property || this.range_steps === 2)
{ {
this.controlType = ControlType.OnOffSwitch; this.controlType = ControlType.OnOffSwitch;
} }
@@ -276,6 +294,10 @@ export class UiControl implements Deserializable<UiControl> {
port_group: string = ""; port_group: string = "";
units: Units = Units.none; units: Units = Units.none;
comment: string = ""; comment: string = "";
is_bypass: boolean = true;
is_program_controller: boolean = true;
custom_units: string = "";
// Return the value of the closest scale_point. // Return the value of the closest scale_point.
clampSelectValue(value: number): number{ clampSelectValue(value: number): number{
@@ -452,6 +474,7 @@ export class UiPlugin implements Deserializable<UiPlugin> {
this.description = input.description; this.description = input.description;
this.controls = UiControl.deserialize_array(input.controls); this.controls = UiControl.deserialize_array(input.controls);
this.port_groups = PortGroup.deserialize_array(input.port_groups); this.port_groups = PortGroup.deserialize_array(input.port_groups);
this.is_vst3 = input.is_vst3;
return this; return this;
} }
@@ -501,5 +524,6 @@ export class UiPlugin implements Deserializable<UiPlugin> {
description: string = ""; description: string = "";
controls: UiControl[] = []; controls: UiControl[] = [];
port_groups: PortGroup[] = []; port_groups: PortGroup[] = [];
is_vst3 : boolean = false;
} }
+2
View File
@@ -67,6 +67,7 @@ export class PedalBoardItem implements Deserializable<PedalBoardItem> {
this.midiBindings = MidiBinding.deserialize_array(input.midiBindings); this.midiBindings = MidiBinding.deserialize_array(input.midiBindings);
this.controlValues = ControlValue.deserializeArray(input.controlValues); this.controlValues = ControlValue.deserializeArray(input.controlValues);
this.vstState = input.vstState ?? "";
return this; return this;
} }
deserialize(input: any): PedalBoardItem { deserialize(input: any): PedalBoardItem {
@@ -183,6 +184,7 @@ export class PedalBoardItem implements Deserializable<PedalBoardItem> {
pluginName?: string; pluginName?: string;
controlValues: ControlValue[] = ControlValue.EmptyArray; controlValues: ControlValue[] = ControlValue.EmptyArray;
midiBindings: MidiBinding[] = []; midiBindings: MidiBinding[] = [];
vstState: string = "";
}; };
+115 -97
View File
@@ -62,6 +62,12 @@ const EMPTY_ICON_URL = "img/fx_empty.svg";
const ERROR_ICON_URL = "img/fx_error.svg"; const ERROR_ICON_URL = "img/fx_error.svg";
const TERMINAL_ICON_URL = "img/fx_terminal.svg"; const TERMINAL_ICON_URL = "img/fx_terminal.svg";
function CalculateConnection(numberOfInputs: number, numberOfOutputs: number)
{
let result = Math.min(numberOfInputs, numberOfOutputs);
if (result > 2) result = 2;
return result;
}
const pedalBoardStyles = (theme: Theme) => createStyles({ const pedalBoardStyles = (theme: Theme) => createStyles({
scrollContainer: { scrollContainer: {
@@ -210,10 +216,9 @@ class PedalLayout {
bounds: Rect = new Rect(); bounds: Rect = new Rect();
inputs: number = 0; numberOfInputs: number = 2;
outputs: number = 0; numberOfOutputs: number = 2;
stereoInput: boolean = false;
stereoOutput: boolean = false;
pedalItem?: PedalBoardItem; pedalItem?: PedalBoardItem;
// Split Layout only. // Split Layout only.
@@ -227,8 +232,8 @@ class PedalLayout {
let t: PedalLayout = new PedalLayout(); let t: PedalLayout = new PedalLayout();
t.uri = START_PEDALBOARD_ITEM_URI; t.uri = START_PEDALBOARD_ITEM_URI;
t.iconUrl = TERMINAL_ICON_URL; t.iconUrl = TERMINAL_ICON_URL;
t.inputs = 0; t.numberOfInputs = 0;
t.outputs = 1; t.numberOfOutputs = 2;
return t; return t;
} }
static End(): PedalLayout { static End(): PedalLayout {
@@ -236,8 +241,8 @@ class PedalLayout {
t.pluginType = PluginType.UtilityPlugin; t.pluginType = PluginType.UtilityPlugin;
t.uri = END_PEDALBOARD_ITEM_URI; t.uri = END_PEDALBOARD_ITEM_URI;
t.iconUrl = TERMINAL_ICON_URL; t.iconUrl = TERMINAL_ICON_URL;
t.inputs = 2; t.numberOfInputs = 2;
t.outputs = 0; t.numberOfOutputs = 0;
return t; return t;
} }
constructor(model?: PiPedalModel, pedalItem?: PedalBoardItem) { constructor(model?: PiPedalModel, pedalItem?: PedalBoardItem) {
@@ -256,27 +261,27 @@ class PedalLayout {
this.topChildren = makeChain(model, splitter.topChain); this.topChildren = makeChain(model, splitter.topChain);
this.bottomChildren = makeChain(model, splitter.bottomChain); this.bottomChildren = makeChain(model, splitter.bottomChain);
this.inputs = 2; this.numberOfInputs = 2;
this.outputs = 2; this.numberOfOutputs = 2;
} else if (pedalItem.isEmpty()) { } else if (pedalItem.isEmpty()) {
this.pluginType = PluginType.UtilityPlugin; this.pluginType = PluginType.UtilityPlugin;
this.iconUrl = EMPTY_ICON_URL; this.iconUrl = EMPTY_ICON_URL;
this.inputs = 2; this.numberOfInputs = 2;
this.outputs = 2; this.numberOfOutputs = 2;
} else if (pedalItem.uri === START_PEDALBOARD_ITEM_URI) { } else if (pedalItem.uri === START_PEDALBOARD_ITEM_URI) {
this.pluginType = PluginType.UtilityPlugin; this.pluginType = PluginType.UtilityPlugin;
this.iconUrl = TERMINAL_ICON_URL; this.iconUrl = TERMINAL_ICON_URL;
this.inputs = 0; this.numberOfInputs = 0;
this.outputs = PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length; this.numberOfOutputs = PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length;
} else if (pedalItem.uri === END_PEDALBOARD_ITEM_URI) { } else if (pedalItem.uri === END_PEDALBOARD_ITEM_URI) {
this.pluginType = PluginType.UtilityPlugin; this.pluginType = PluginType.UtilityPlugin;
this.iconUrl = TERMINAL_ICON_URL; this.iconUrl = TERMINAL_ICON_URL;
this.inputs = PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length; this.numberOfInputs = PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length;
this.outputs = 0; this.numberOfOutputs = 0;
} }
else { else {
let uiPlugin = model.getUiPlugin(pedalItem.uri); let uiPlugin = model.getUiPlugin(pedalItem.uri);
@@ -284,15 +289,15 @@ class PedalLayout {
this.pluginType = uiPlugin.plugin_type; this.pluginType = uiPlugin.plugin_type;
this.iconUrl = SelectIcon(uiPlugin.plugin_type,uiPlugin.uri); this.iconUrl = SelectIcon(uiPlugin.plugin_type,uiPlugin.uri);
this.name = uiPlugin.name; this.name = uiPlugin.name;
this.inputs = uiPlugin.audio_inputs; this.numberOfInputs = Math.max(uiPlugin.audio_inputs,2);
this.outputs = uiPlugin.audio_outputs; this.numberOfOutputs = Math.max(uiPlugin.audio_outputs,2);
} else { } else {
// default to empty plugin. // default to empty plugin.
this.pluginType = PluginType.UtilityPlugin; this.pluginType = PluginType.UtilityPlugin;
this.name = pedalItem.pluginName??"#error"; this.name = pedalItem.pluginName??"#error";
this.iconUrl = ERROR_ICON_URL; this.iconUrl = ERROR_ICON_URL;
this.inputs = 2; this.numberOfInputs = 2;
this.outputs = 2; this.numberOfOutputs = 2;
} }
} }
} }
@@ -677,18 +682,18 @@ const PedalBoardView =
// let classes = this.props.classes; // let classes = this.props.classes;
let x_ = item.bounds.x + CELL_WIDTH / 2; let x_ = item.bounds.x + CELL_WIDTH / 2;
let y_ = item.bounds.y + CELL_HEIGHT / 2; let y_ = item.bounds.y + CELL_HEIGHT / 2;
let isStereo = item.stereoOutput; let numberOfOutputs = item.numberOfOutputs;
let color = enabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR; let color = enabled ? ENABLED_CONNECTOR_COLOR : DISABLED_CONNECTOR_COLOR;
let svgPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_ + CELL_WIDTH, y_).toString(); let svgPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_ + CELL_WIDTH, y_).toString();
if (isStereo) { if (numberOfOutputs === 2) {
output.push(( output.push((
<path d={svgPath} stroke={color} strokeWidth={SVG_STEREO_STROKE_WIDTH} /> <path d={svgPath} stroke={color} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
)); ));
output.push(( output.push((
<path d={svgPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} /> <path d={svgPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
)); ));
} else { } else if (numberOfOutputs === 1) {
output.push(( output.push((
<path d={svgPath} stroke={color} strokeWidth={SVG_STROKE_WIDTH} /> <path d={svgPath} stroke={color} strokeWidth={SVG_STROKE_WIDTH} />
)); ));
@@ -712,18 +717,18 @@ const PedalBoardView =
let topStartPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_, yTop).lineTo(x_ + CELL_WIDTH, yTop).toString(); let topStartPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_, yTop).lineTo(x_ + CELL_WIDTH, yTop).toString();
let bottomStartPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_, yBottom).lineTo(x_ + CELL_WIDTH, yBottom).toString(); let bottomStartPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_, yBottom).lineTo(x_ + CELL_WIDTH, yBottom).toString();
if (item.stereoInput && item.topChildren[0].stereoInput) { if (item.numberOfInputs === 2 && item.topChildren[0].numberOfInputs === 2) {
output.push((<path d={topStartPath} stroke={topColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />)); output.push((<path d={topStartPath} stroke={topColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />));
output.push((<path d={topStartPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />)); output.push((<path d={topStartPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />));
} else { } else if (item.numberOfInputs !== 0 && item.topChildren[0].numberOfInputs !== 0) {
output.push((<path d={topStartPath} stroke={topColor} strokeWidth={SVG_STROKE_WIDTH} />)); output.push((<path d={topStartPath} stroke={topColor} strokeWidth={SVG_STROKE_WIDTH} />));
} }
if (item.stereoInput && item.bottomChildren[0].stereoInput) { if (item.numberOfInputs === 2 && item.bottomChildren[0].numberOfInputs === 2) {
output.push((<path d={bottomStartPath} stroke={bottomColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />)); output.push((<path d={bottomStartPath} stroke={bottomColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />));
output.push((<path d={bottomStartPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />)); output.push((<path d={bottomStartPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />));
} else { } else if (item.numberOfInputs !== 0 && item.bottomChildren[0].numberOfInputs !== 0) {
output.push((<path d={bottomStartPath} stroke={bottomColor} strokeWidth={SVG_STROKE_WIDTH} />)); output.push((<path d={bottomStartPath} stroke={bottomColor} strokeWidth={SVG_STROKE_WIDTH} />));
} }
@@ -741,6 +746,8 @@ const PedalBoardView =
let firstPathStereo: boolean; let firstPathStereo: boolean;
let secondPathStereo: boolean; let secondPathStereo: boolean;
let firstPathAbsent: boolean;
let secondPathAbsent: boolean;
let firstPathEnabled: boolean; let firstPathEnabled: boolean;
let secondPathEnabled: boolean; let secondPathEnabled: boolean;
let xTee: number; let xTee: number;
@@ -753,11 +760,13 @@ const PedalBoardView =
// Third case: L/R stereo output, when both outputs are mono, requires a third stroke. // Third case: L/R stereo output, when both outputs are mono, requires a third stroke.
let thirdPath: string | null = null; // for L/R stereo output (which can be stereo even if both outputs are mono) let thirdPath: string | null = null; // for L/R stereo output (which can be stereo even if both outputs are mono)
let hasThirdPath = item.stereoOutput && (!lastTop.stereoOutput) && (!lastBottom.stereoOutput); let hasThirdPath = item.numberOfOutputs === 2 && (lastTop.numberOfOutputs !== 2) && (lastBottom.numberOfOutputs !== 2);
if (hasThirdPath) { if (hasThirdPath) {
firstPathStereo = false; firstPathStereo = false;
secondPathStereo = false; secondPathStereo = false;
firstPathAbsent = lastTop.numberOfOutputs === 0 || item.numberOfOutputs === 0;
secondPathAbsent = lastBottom.numberOfOutputs === 0 || item.numberOfOutputs === 0;
xTee = xTee0 - monoAdjustment; xTee = xTee0 - monoAdjustment;
firstPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).toString(); firstPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).toString();
@@ -767,10 +776,13 @@ const PedalBoardView =
thirdPath= new SvgPathBuilder().moveTo(xTee0,y_).lineTo(xEnd,y_).toString(); thirdPath= new SvgPathBuilder().moveTo(xTee0,y_).lineTo(xEnd,y_).toString();
firstPathEnabled = bottomEnabled; firstPathEnabled = bottomEnabled;
secondPathEnabled = topEnabled; secondPathEnabled = topEnabled;
} else if (bottomPathFirst || (topEnabled && lastTop.stereoOutput)) { } else if (bottomPathFirst || (topEnabled && lastTop.numberOfOutputs === 2)) {
// draw the bottom path first. // draw the bottom path first.
firstPathStereo = item.stereoOutput && lastBottom.stereoOutput; firstPathStereo = item.numberOfOutputs === 2 && lastBottom.numberOfOutputs === 2;
secondPathStereo = item.stereoOutput && lastTop.stereoOutput; secondPathStereo = item.numberOfOutputs === 2 && lastTop.numberOfOutputs === 2;
firstPathAbsent = item.numberOfOutputs === 0 || lastBottom.numberOfOutputs === 0;
secondPathAbsent = item.numberOfOutputs === 0 || lastTop.numberOfOutputs === 0;
xTee = firstPathStereo ? xTee0 : xTee0 - monoAdjustment; xTee = firstPathStereo ? xTee0 : xTee0 - monoAdjustment;
firstPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).toString(); firstPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).toString();
xTee = secondPathStereo ? xTee0 : xTee0 - monoAdjustment; xTee = secondPathStereo ? xTee0 : xTee0 - monoAdjustment;
@@ -780,8 +792,10 @@ const PedalBoardView =
secondPathEnabled = topEnabled; secondPathEnabled = topEnabled;
} else { } else {
// draw the top path first. // draw the top path first.
firstPathStereo = item.stereoOutput && lastTop.stereoOutput; firstPathStereo = item.numberOfOutputs === 2 && lastTop.numberOfOutputs === 2;
secondPathStereo = item.stereoOutput && lastBottom.stereoOutput; secondPathStereo = item.numberOfOutputs === 2 && lastBottom.numberOfOutputs === 2;
firstPathAbsent = item.numberOfOutputs === 0 || lastTop.numberOfOutputs === 0;
secondPathAbsent = item.numberOfOutputs === 0 || lastBottom.numberOfOutputs === 0;
xTee = firstPathStereo ? xTee0 : xTee0 - monoAdjustment; xTee = firstPathStereo ? xTee0 : xTee0 - monoAdjustment;
firstPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).toString(); firstPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).toString();
@@ -804,7 +818,7 @@ const PedalBoardView =
output.push(( output.push((
<path d={firstPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} /> <path d={firstPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
)); ));
} else { } else if (!firstPathAbsent) {
output.push(( output.push((
<path d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STROKE_WIDTH} /> <path d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STROKE_WIDTH} />
)); ));
@@ -816,7 +830,7 @@ const PedalBoardView =
output.push(( output.push((
<path d={secondPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} /> <path d={secondPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
)); ));
} else { } else if (!secondPathAbsent) {
output.push(( output.push((
<path d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STROKE_WIDTH} /> <path d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STROKE_WIDTH} />
)); ));
@@ -828,7 +842,7 @@ const PedalBoardView =
output.push(( output.push((
<path d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} /> <path d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
)); ));
} else { } else if (!firstPathAbsent) {
output.push(( output.push((
<path d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STROKE_WIDTH} /> <path d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STROKE_WIDTH} />
)); ));
@@ -837,7 +851,7 @@ const PedalBoardView =
output.push(( output.push((
<path d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} /> <path d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
)); ));
} else { } else if (!secondPathAbsent) {
output.push(( output.push((
<path d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STROKE_WIDTH} /> <path d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STROKE_WIDTH} />
)); ));
@@ -1011,126 +1025,130 @@ const PedalBoardView =
} }
return true; return true;
} }
canOutputStereo(item: PedalLayout): boolean { getNumberOfInputs(item: PedalLayout): number {
if (item.pedalItem) { if (item.pedalItem) {
let plugin = this.model.getUiPlugin(item.pedalItem.uri); let plugin = this.model.getUiPlugin(item.pedalItem.uri);
if (plugin) { if (plugin) {
return plugin.audio_outputs === 2; return plugin.audio_inputs;
} }
} }
return true; return 1;
}
getNumberOfOutputs(item: PedalLayout): number {
if (item.pedalItem) {
let plugin = this.model.getUiPlugin(item.pedalItem.uri);
if (plugin) {
return plugin.audio_outputs;
}
}
return 1;
} }
markStereoOutputs(layoutChain: PedalLayout[], stereoInput: boolean, stereoOutput: boolean) { markStereoOutputs(layoutChain: PedalLayout[], numberOfInputs: number, numberOfOutputs: number) {
// analyze forward flow. // analyze forward flow.
this.markStereoForward(layoutChain, stereoInput); this.markStereoForward(layoutChain, numberOfInputs);
// mark items that feed a mono effect as mono. // mark items that feed a mono effect as mono.
this.markStereoBackward(layoutChain, stereoOutput); this.markStereoBackward(layoutChain, numberOfOutputs);
} }
markStereoBackward(layoutChain: PedalLayout[], stereoOutput: boolean) : boolean markStereoBackward(layoutChain: PedalLayout[], numberOfOutputs: number) : number
{ {
for (let i = layoutChain.length-1; i >= 0; --i) for (let i = layoutChain.length-1; i >= 0; --i)
{ {
let item = layoutChain[i]; let item = layoutChain[i];
if (item.isSplitter()) if (item.isSplitter())
{ {
if (!stereoOutput) item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs)
{
item.stereoOutput = false;
}
this.markStereoBackward(item.topChildren,stereoOutput);
this.markStereoBackward(item.bottomChildren,stereoOutput);
let topStereoInput = item.topChildren[0].stereoInput;
let bottomStereoInput = item.bottomChildren[0].stereoInput;
if (! (topStereoInput || bottomStereoInput)) this.markStereoBackward(item.topChildren,numberOfOutputs);
this.markStereoBackward(item.bottomChildren,numberOfOutputs);
let topInputs = item.topChildren[0].numberOfInputs;
let bottomInputs = item.bottomChildren[0].numberOfInputs;
let splitItem = item.pedalItem as PedalBoardSplitItem;
if (splitItem.getSplitType() !== SplitType.Lr)
{ {
let splitItem = item.pedalItem as PedalBoardSplitItem; item.numberOfInputs = CalculateConnection(item.numberOfInputs, Math.max(topInputs,bottomInputs));
if (splitItem.getSplitType() !== SplitType.Lr)
{
item.stereoInput = false;
}
} }
} else if (item.isEnd()) } else if (item.isEnd())
{ {
} else if (item.isStart()) } else if (item.isStart())
{ {
if (!stereoOutput) item.numberOfOutputs = CalculateConnection(item.numberOfOutputs,numberOfOutputs);
{ return item.numberOfOutputs;
item.stereoOutput = false;
}
return item.stereoOutput;
} else if (item.isEmpty()) { } else if (item.isEmpty()) {
if (!stereoOutput) if (numberOfOutputs === 0)
{ {
item.stereoOutput = false; item.numberOfOutputs = 0;
item.stereoInput = false; item.numberOfInputs = CalculateConnection(item.numberOfInputs,2);
} else {
item.numberOfOutputs = CalculateConnection(item.numberOfOutputs,numberOfOutputs);
item.numberOfInputs = CalculateConnection(item.numberOfInputs,numberOfOutputs);
} }
} else { } else {
if (!stereoOutput) item.numberOfOutputs = CalculateConnection(item.numberOfOutputs,numberOfOutputs);
{
item.stereoOutput = false;
}
} }
stereoOutput = item.stereoInput; numberOfOutputs = item.numberOfInputs;
} }
return stereoOutput; return numberOfOutputs;
} }
markStereoForward(layoutChain: PedalLayout[],stereoInput: boolean): boolean markStereoForward(layoutChain: PedalLayout[],numberOfInputs: number): number
{ {
if (layoutChain.length === 0) { if (layoutChain.length === 0) {
return stereoInput; return numberOfInputs;
} }
for (let i = 0; i < layoutChain.length; ++i) { for (let i = 0; i < layoutChain.length; ++i) {
let item = layoutChain[i]; let item = layoutChain[i];
if (item.isSplitter()) { if (item.isSplitter()) {
let splitter = item.pedalItem as PedalBoardSplitItem; let splitter = item.pedalItem as PedalBoardSplitItem;
item.stereoInput = stereoInput; item.numberOfInputs = numberOfInputs;
let isChainInputStereo = stereoInput; let chainInputs = numberOfInputs;
if (splitter.getSplitType() === SplitType.Lr) if (splitter.getSplitType() === SplitType.Lr)
{ {
isChainInputStereo = false; chainInputs = CalculateConnection(numberOfInputs,1);
} }
let topOutputs = this.markStereoForward(item.topChildren, chainInputs);
let topStereo = this.markStereoForward(item.topChildren, isChainInputStereo); let bottomOutputs = this.markStereoForward(item.bottomChildren, chainInputs);
let bottomStereo = this.markStereoForward(item.bottomChildren, isChainInputStereo);
if (splitter.getSplitType() === SplitType.Ab) { if (splitter.getSplitType() === SplitType.Ab) {
if (splitter.isASelected()) { if (splitter.isASelected()) {
item.stereoOutput = topStereo; item.numberOfOutputs = topOutputs;
} else { } else {
item.stereoOutput = bottomStereo; item.numberOfOutputs = bottomOutputs;
} }
} else if (splitter.getSplitType() === SplitType.Lr) { } else if (splitter.getSplitType() === SplitType.Lr) {
item.stereoOutput = true; item.numberOfOutputs = (topOutputs !== 0 && bottomOutputs !== 0) ? 2: 0;
} else { } else {
item.stereoOutput = topStereo || bottomStereo; item.numberOfOutputs = CalculateConnection(topOutputs,bottomOutputs);
} }
} else if (item.isStart()) } else if (item.isStart())
{ {
item.stereoOutput = (PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length > 1); item.numberOfOutputs = Math.min(PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length,2);
} else if (item.isEnd()) { } else if (item.isEnd()) {
item.stereoInput = (PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length > 1) && stereoInput; item.numberOfInputs =
return item.stereoInput; CalculateConnection(
Math.min(PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length,2),
numberOfInputs);
return item.numberOfInputs;
} else if (item.isEmpty()) { } else if (item.isEmpty()) {
item.stereoInput = stereoInput; item.numberOfInputs = numberOfInputs;
item.stereoOutput = stereoInput; if (numberOfInputs === 0)
} else { {
if (stereoInput && this.canOutputStereo(item)) { item.numberOfOutputs = 2;
item.stereoInput = true;
} else { } else {
item.stereoInput = false; item.numberOfOutputs = item.numberOfInputs;
} }
item.stereoOutput = this.canOutputStereo(item); } else {
item.numberOfInputs = CalculateConnection(numberOfInputs,this.getNumberOfInputs(item));
item.numberOfOutputs = this.getNumberOfOutputs(item);
} }
stereoInput = item.stereoOutput; numberOfInputs = item.numberOfOutputs;
} }
return stereoInput; return numberOfInputs;
} }
currentLayout?: PedalLayout[]; currentLayout?: PedalLayout[];
@@ -1145,7 +1163,7 @@ const PedalBoardView =
{ {
layoutChain.splice(0, 0, start); layoutChain.splice(0, 0, start);
layoutChain.splice(layoutChain.length, 0, end); layoutChain.splice(layoutChain.length, 0, end);
this.markStereoOutputs(layoutChain, true, true); this.markStereoOutputs(layoutChain, 2,2);
} }
let layoutSize = this.doLayout(layoutChain); let layoutSize = this.doLayout(layoutChain);
+80 -2
View File
@@ -22,7 +22,7 @@ import { UiPlugin, UiControl, PluginType } from './Lv2Plugin';
import { PiPedalArgumentError, PiPedalStateError } from './PiPedalError'; import { PiPedalArgumentError, PiPedalStateError } from './PiPedalError';
import { ObservableProperty } from './ObservableProperty'; import { ObservableProperty } from './ObservableProperty';
import { PedalBoard, ControlValue } from './PedalBoard' import { PedalBoard, PedalBoardItem, ControlValue } from './PedalBoard'
import PluginClass from './PluginClass'; import PluginClass from './PluginClass';
import PiPedalSocket, { PiPedalMessageHeader } from './PiPedalSocket'; import PiPedalSocket, { PiPedalMessageHeader } from './PiPedalSocket';
import { nullCast } from './Utility' import { nullCast } from './Utility'
@@ -303,6 +303,13 @@ interface ControlChangedBody {
symbol: string; symbol: string;
value: number; value: number;
}; };
interface Vst3ControlChangedBody {
clientId: number;
instanceId: number;
symbol: string;
value: number;
state: string;
};
export interface FavoritesList { export interface FavoritesList {
[url: string]: boolean; [url: string]: boolean;
@@ -634,6 +641,16 @@ class PiPedalModelImpl implements PiPedalModel {
controlChangedBody.value, controlChangedBody.value,
false // do NOT notify the server of the change. false // do NOT notify the server of the change.
); );
} else if (message === "onVst3ControlChanged") {
let controlChangedBody = body as Vst3ControlChangedBody;
this._setVst3PedalBoardControlValue(
controlChangedBody.instanceId,
controlChangedBody.symbol,
controlChangedBody.value,
controlChangedBody.state,
false // do NOT notify the server of the change.
);
} else if (message === "onJackServerSettingsChanged") { } else if (message === "onJackServerSettingsChanged") {
let jackServerSettings = new JackServerSettings().deserialize(body); let jackServerSettings = new JackServerSettings().deserialize(body);
this.jackServerSettings.set(jackServerSettings); this.jackServerSettings.set(jackServerSettings);
@@ -1195,6 +1212,29 @@ class PiPedalModelImpl implements PiPedalModel {
} }
} }
}
_setVst3PedalBoardControlValue(instanceId: number, key: string, value: number, state: string, notifyServer: boolean ): void {
let pedalBoard = this.pedalBoard.get();
if (pedalBoard === undefined) throw new PiPedalStateError("Pedalboard not ready.");
let newPedalBoard = pedalBoard.clone();
let item: PedalBoardItem = newPedalBoard.getItem(instanceId);
let changed = item.setControlValue(key, value);
item.vstState = state;
if (changed) {
this.pedalBoard.set(newPedalBoard);
if (notifyServer) {
this._setServerControl("setControl", instanceId, key, value);
}
for (let i = 0; i < this._controlValueChangeItems.length; ++i) {
let item = this._controlValueChangeItems[i];
if (instanceId === item.instanceId) {
item.onValueChanged(key, value);
}
}
}
} }
setPedalBoardControlValue(instanceId: number, key: string, value: number): void { setPedalBoardControlValue(instanceId: number, key: string, value: number): void {
@@ -1232,7 +1272,7 @@ class PiPedalModelImpl implements PiPedalModel {
clientId: this.clientId, clientId: this.clientId,
pedalBoard: this.pedalBoard.get() pedalBoard: this.pedalBoard.get()
}; };
this.webSocket?.send("setCurrentPedalBoard", body); this.webSocket?.send("updateCurrentPedalBoard", body);
} }
@@ -1288,6 +1328,8 @@ class PiPedalModelImpl implements PiPedalModel {
deletePedalBoardPedal(instanceId: number): number | null { deletePedalBoardPedal(instanceId: number): number | null {
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let result = newPedalBoard.deleteItem(instanceId); let result = newPedalBoard.deleteItem(instanceId);
if (result != null) { if (result != null) {
this.pedalBoard.set(newPedalBoard); this.pedalBoard.set(newPedalBoard);
@@ -1301,6 +1343,9 @@ class PiPedalModelImpl implements PiPedalModel {
if (fromInstanceId === toInstanceId) return; if (fromInstanceId === toInstanceId) return;
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(fromInstanceId); let fromItem = newPedalBoard.getItem(fromInstanceId);
if (fromItem === null) { if (fromItem === null) {
throw new PiPedalArgumentError("fromInstanceId not found."); throw new PiPedalArgumentError("fromInstanceId not found.");
@@ -1317,6 +1362,9 @@ class PiPedalModelImpl implements PiPedalModel {
if (fromInstanceId === toInstanceId) return; if (fromInstanceId === toInstanceId) return;
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(fromInstanceId); let fromItem = newPedalBoard.getItem(fromInstanceId);
if (fromItem === null) { if (fromItem === null) {
throw new PiPedalArgumentError("fromInstanceId not found."); throw new PiPedalArgumentError("fromInstanceId not found.");
@@ -1334,6 +1382,10 @@ class PiPedalModelImpl implements PiPedalModel {
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(instanceId); let fromItem = newPedalBoard.getItem(instanceId);
if (fromItem === null) { if (fromItem === null) {
throw new PiPedalArgumentError("fromInstanceId not found."); throw new PiPedalArgumentError("fromInstanceId not found.");
@@ -1350,6 +1402,9 @@ class PiPedalModelImpl implements PiPedalModel {
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(instanceId); let fromItem = newPedalBoard.getItem(instanceId);
if (fromItem === null) { if (fromItem === null) {
throw new PiPedalArgumentError("fromInstanceId not found."); throw new PiPedalArgumentError("fromInstanceId not found.");
@@ -1371,6 +1426,9 @@ class PiPedalModelImpl implements PiPedalModel {
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(fromInstanceId); let fromItem = newPedalBoard.getItem(fromInstanceId);
let toItem = newPedalBoard.getItem(toInstanceId); let toItem = newPedalBoard.getItem(toInstanceId);
if (fromItem === null) { if (fromItem === null) {
@@ -1389,6 +1447,9 @@ class PiPedalModelImpl implements PiPedalModel {
addPedalBoardItem(instanceId: number, append: boolean): number { addPedalBoardItem(instanceId: number, append: boolean): number {
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let item = newPedalBoard.getItem(instanceId); let item = newPedalBoard.getItem(instanceId);
if (item === null) { if (item === null) {
throw new PiPedalArgumentError("instanceId not found."); throw new PiPedalArgumentError("instanceId not found.");
@@ -1402,6 +1463,8 @@ class PiPedalModelImpl implements PiPedalModel {
addPedalBoardSplitItem(instanceId: number, append: boolean): number { addPedalBoardSplitItem(instanceId: number, append: boolean): number {
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let item = newPedalBoard.getItem(instanceId); let item = newPedalBoard.getItem(instanceId);
if (item === null) { if (item === null) {
throw new PiPedalArgumentError("instanceId not found."); throw new PiPedalArgumentError("instanceId not found.");
@@ -1416,6 +1479,9 @@ class PiPedalModelImpl implements PiPedalModel {
setPedalBoardItemEmpty(instanceId: number): number { setPedalBoardItemEmpty(instanceId: number): number {
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let item = newPedalBoard.getItem(instanceId); let item = newPedalBoard.getItem(instanceId);
if (item === null) { if (item === null) {
throw new PiPedalArgumentError("instanceId not found."); throw new PiPedalArgumentError("instanceId not found.");
@@ -1768,12 +1834,24 @@ class PiPedalModelImpl implements PiPedalModel {
this.showAlert(error); this.showAlert(error);
}); });
} }
updateVst3State(pedalBoard: PedalBoard)
{
// let it = pedalBoard.itemsGenerator();
// while (true) {
// let v = it.next();
// if (v.done) break;
// let item = v.value;
// }
}
setMidiBinding(instanceId: number, midiBinding: MidiBinding): void { setMidiBinding(instanceId: number, midiBinding: MidiBinding): void {
let pedalBoard = this.pedalBoard.get(); let pedalBoard = this.pedalBoard.get();
if (!pedalBoard) { if (!pedalBoard) {
throw new PiPedalStateError("Pedalboard not loaded."); throw new PiPedalStateError("Pedalboard not loaded.");
} }
let newPedalBoard = pedalBoard.clone(); let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
if (newPedalBoard.setMidiBinding(instanceId, midiBinding)) { if (newPedalBoard.setMidiBinding(instanceId, midiBinding)) {
this.pedalBoard.set(newPedalBoard); this.pedalBoard.set(newPedalBoard);
// notify the server. // notify the server.
+14 -8
View File
@@ -603,17 +603,23 @@ const PluginControl =
let uiControl = this.props.uiControl; let uiControl = this.props.uiControl;
if (uiControl) { if (uiControl) {
if (uiControl.range_steps > 1) { if (uiControl.range_steps > 1) {
range = Math.round(range * uiControl.range_steps-1) / (uiControl.range_steps-1); range = Math.round(range * (uiControl.range_steps-1)) / (uiControl.range_steps-1);
} }
let value: number; let value: number;
if (uiControl.is_logarithmic) if (uiControl.min_value === uiControl.max_value)
{ {
value = uiControl.min_value*Math.pow(uiControl.max_value/uiControl.min_value,range); value = uiControl.min_value;
} else { } else
value = range * (uiControl.max_value - uiControl.min_value) + uiControl.min_value; {
} if (uiControl.is_logarithmic)
if (uiControl.integer_property) { {
value = Math.round(value); value = uiControl.min_value*Math.pow(uiControl.max_value/uiControl.min_value,range);
} else {
value = range * (uiControl.max_value - uiControl.min_value) + uiControl.min_value;
}
if (uiControl.integer_property) {
value = Math.round(value);
}
} }
return value; return value;
} }
+2 -1
View File
@@ -29,6 +29,7 @@
#include "AlsaDriver.hpp" #include "AlsaDriver.hpp"
#include "JackServerSettings.hpp" #include "JackServerSettings.hpp"
#include <thread> #include <thread>
#include "RtInversionGuard.hpp"
#include "CpuUse.hpp" #include "CpuUse.hpp"
@@ -967,7 +968,7 @@ namespace pipedal
struct sched_param param; struct sched_param param;
memset(&param, 0, sizeof(param)); memset(&param, 0, sizeof(param));
param.sched_priority = 79; param.sched_priority = RT_THREAD_PRIORITY;
int result = sched_setscheduler(0, SCHED_RR, &param); int result = sched_setscheduler(0, SCHED_RR, &param);
if (result == 0) if (result == 0)
+279 -8
View File
@@ -7,6 +7,37 @@ set (USE_PCH 1)
include(FindPkgConfig) include(FindPkgConfig)
#################################################################
# ENABLE/DISABLE VST3 Support.
# Disabled, pending Steinberg licensing agreement implementation
# and approval..
# Do not enable unless you have non-GPL3 access to the Steinberg
# SDK.
#################################################################
set (ENABLE_VST3 0)
if (ENABLE_VST3)
set (VST3PATH "../../vst3sdk")
message(STATUS " VST3 Support Enabled")
set (VST3_INCLUDES "${VST3PATH}")
if (CMAKE_BUILD_TYPE MATCHES Debug)
add_compile_definitions("DEVELOPMENT")
else()
add_compile_definitions("RELEASE")
endif()
add_compile_definitions("ENABLE_VST3" )
#add_compile_definitions("_GLIBCXX_USE_CXX11_ABI")
else()
message(STATUS " VST3 Support Disabled")
set (VST3_INCLUDES "")
endif()
########### VST 3 SUPPORT #############################################3
# Can't get the pkg_check to work. # Can't get the pkg_check to work.
@@ -60,19 +91,27 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True) set(CMAKE_CXX_STANDARD_REQUIRED True)
set (USE_SANITIZE False) set (USE_SANITIZE False)
if (!ENABLE_VST3)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wno-psabi")
else()
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-multichar -Wno-psabi")
endif()
if (CMAKE_BUILD_TYPE MATCHES Debug) if (CMAKE_BUILD_TYPE MATCHES Debug)
message(STATUS "Debug build") message(STATUS "Debug build")
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wno-psabi -O0 -DDEBUG -Werror" ) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -DDEBUG" )
if (USE_SANITIZE) if (USE_SANITIZE)
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-fsanitize=address -static-libasan " ) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -static-libasan " )
endif() endif()
elseif(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo) elseif(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
message(STATUS "RelWithgDebInfo build") message(STATUS "RelWithgDebInfo build")
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wno-psabi -Werror" ) #set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "" )
else() else()
message(STATUS "Release build") message(STATUS "Release build")
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wno-psabi -Werror" ) #set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "" )
endif() endif()
@@ -84,8 +123,19 @@ message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}" )
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_BIND_GLOBAL_PLACEHOLDERS -DONBOARDING) add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_BIND_GLOBAL_PLACEHOLDERS -DONBOARDING)
if (VST3PATH)
set (VST3_SOURCES
vst3/Vst3Effect.hpp Vst3Effect.cpp
vst3/Vst3Host.hpp Vst3Host.cpp
Vst3MidiToEvent.hpp
vst3/Vst3RtStream.hpp Vst3RtStream.cpp
)
else()
set (VST3_SOURCES)
endif()
set (PIPEDAL_SOURCES set (PIPEDAL_SOURCES
RtInversionGuard.hpp
CpuUse.hpp CpuUse.cpp CpuUse.hpp CpuUse.cpp
P2pConfigFiles.hpp P2pConfigFiles.hpp
AvahiService.cpp AvahiService.hpp AvahiService.cpp AvahiService.hpp
@@ -104,7 +154,7 @@ set (PIPEDAL_SOURCES
WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp
ConfigUtil.hpp ConfigUtil.cpp ConfigUtil.hpp ConfigUtil.cpp
RequestHandler.hpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp RequestHandler.hpp json.cpp json.hpp Scratch.cpp PiPedalHost.hpp PiPedalHost.cpp
PluginType.hpp PluginType.cpp PluginType.hpp PluginType.cpp
Lv2Log.hpp Lv2Log.cpp Lv2Log.hpp Lv2Log.cpp
PiPedalSocket.hpp PiPedalSocket.cpp PiPedalSocket.hpp PiPedalSocket.cpp
@@ -152,6 +202,7 @@ set (PIPEDAL_SOURCES
AudioDriver.hpp AudioDriver.hpp
AudioConfig.hpp AudioConfig.hpp
${VST3_SOURCES}
) )
@@ -161,16 +212,25 @@ include_directories( ${pipedald_SOURCE_DIR}/. ../build/src)
set (PIPEDAL_INCLUDES set (PIPEDAL_INCLUDES
${LV2DEV_INCLUDE_DIRS} ${LV2DEV_INCLUDE_DIRS}
${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS} ${LVDEV_INCLUDE_DIRS} ${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS} ${LVDEV_INCLUDE_DIRS}
${VST3_INCLUDES}
.
) )
set(PIPEDAL_LIBS libpipedald pthread atomic stdc++fs asound avahi-common avahi-client systemd set(PIPEDAL_LIBS libpipedald
pthread atomic stdc++fs asound avahi-common avahi-client systemd
${LILV_0_LIBRARIES} ${LILV_0_LIBRARIES}
# ${JACK_LIBRARIES} # ${JACK_LIBRARIES}
${LIBNL3_LIBRARIES} ) ${LIBNL3_LIBRARIES}
)
if (VST3_ENABLED)
set (PIPEDAL_LIBS vst3_lib ${PIPEDAL_LIBS})
endif()
########################## ##########################
add_library(libpipedald STATIC ${PIPEDAL_SOURCES}) add_library(libpipedald STATIC ${PIPEDAL_SOURCES})
target_include_directories(libpipedald PRIVATE ${PIPEDAL_INCLUDES} target_include_directories(libpipedald PRIVATE ${PIPEDAL_INCLUDES}
@@ -226,6 +286,217 @@ target_link_libraries(pipedaltest PRIVATE ${PIPEDAL_LIBS}
) )
#########################################################
# VST3 Source.
#
# VST3 support is optional, because it poses unfortunate
# licensing problems.
# There's no easy way to get the VST SDK to build without
# GCC c++11 linkage, which is not compatible with pipedal's
# use of C++20 features (strictly a link time breakage).
#
# So pipedal rebuilds the VST3 SDK using pipedal-compatible
# compile flags.
#
# The current build supports only Linux builds. Refer to the
# original VST3 SDK build for details on how to build
# for other platforms.
#
#########################################################
if(ENABLE_VST3)
set (VST3_SRCPATH "${VST3PATH}/public.sdk/source")
set (VST3_BASEPATH "${VST3PATH}/base")
set (VST3_PLUGININTERFACES_BASE_PATH "${VST3PATH}/pluginterfaces/base")
set(vst3_sources
${VST3_SRCPATH}/vst/vstpresetfile.cpp
${VST3_SRCPATH}/vst/vstpresetfile.h
# base files.
${VST3_BASEPATH}/source/baseiids.cpp
${VST3_BASEPATH}/source/classfactoryhelpers.h
${VST3_BASEPATH}/source/fbuffer.cpp
${VST3_BASEPATH}/source/fbuffer.h
${VST3_BASEPATH}/source/fcleanup.h
${VST3_BASEPATH}/source/fcommandline.h
${VST3_BASEPATH}/source/fdebug.cpp
${VST3_BASEPATH}/source/fdebug.h
${VST3_BASEPATH}/source/fdynlib.cpp
${VST3_BASEPATH}/source/fdynlib.h
${VST3_BASEPATH}/source/fobject.cpp
${VST3_BASEPATH}/source/fobject.h
${VST3_BASEPATH}/source/fstreamer.cpp
${VST3_BASEPATH}/source/fstreamer.h
${VST3_BASEPATH}/source/fstring.cpp
${VST3_BASEPATH}/source/fstring.h
${VST3_BASEPATH}/source/timer.cpp
${VST3_BASEPATH}/source/timer.h
${VST3_BASEPATH}/source/updatehandler.cpp
${VST3_BASEPATH}/source/updatehandler.h
${VST3_BASEPATH}/thread/include/fcondition.h
${VST3_BASEPATH}/thread/include/flock.h
${VST3_BASEPATH}/thread/source/fcondition.cpp
${VST3_BASEPATH}/thread/source/flock.cpp
# sdk_common files.
${VST3_SRCPATH}/common/commoniids.cpp
${VST3_SRCPATH}/common/openurl.cpp
${VST3_SRCPATH}/common/openurl.h
${VST3_SRCPATH}/common/systemclipboard.h
${VST3_SRCPATH}/common/systemclipboard_win32.cpp
${VST3_SRCPATH}/common/threadchecker_linux.cpp
${VST3_SRCPATH}/common/threadchecker_win32.cpp
${VST3_SRCPATH}/common/threadchecker.h
${VST3_SRCPATH}/common/memorystream.cpp
${VST3_SRCPATH}/common/memorystream.h
# pluginterfaces
${VST3_PLUGININTERFACES_BASE_PATH}/conststringtable.cpp
${VST3_PLUGININTERFACES_BASE_PATH}/conststringtable.h
${VST3_PLUGININTERFACES_BASE_PATH}/coreiids.cpp
${VST3_PLUGININTERFACES_BASE_PATH}/falignpop.h
${VST3_PLUGININTERFACES_BASE_PATH}/falignpush.h
${VST3_PLUGININTERFACES_BASE_PATH}/fplatform.h
${VST3_PLUGININTERFACES_BASE_PATH}/fstrdefs.h
${VST3_PLUGININTERFACES_BASE_PATH}/ftypes.h
${VST3_PLUGININTERFACES_BASE_PATH}/funknown.cpp
${VST3_PLUGININTERFACES_BASE_PATH}/funknown.h
${VST3_PLUGININTERFACES_BASE_PATH}/funknownimpl.h
${VST3_PLUGININTERFACES_BASE_PATH}/futils.h
${VST3_PLUGININTERFACES_BASE_PATH}/fvariant.h
${VST3_PLUGININTERFACES_BASE_PATH}/geoconstants.h
${VST3_PLUGININTERFACES_BASE_PATH}/ibstream.h
${VST3_PLUGININTERFACES_BASE_PATH}/icloneable.h
${VST3_PLUGININTERFACES_BASE_PATH}/ierrorcontext.h
${VST3_PLUGININTERFACES_BASE_PATH}/ipersistent.h
${VST3_PLUGININTERFACES_BASE_PATH}/ipluginbase.h
${VST3_PLUGININTERFACES_BASE_PATH}/istringresult.h
${VST3_PLUGININTERFACES_BASE_PATH}/iupdatehandler.h
${VST3_PLUGININTERFACES_BASE_PATH}/keycodes.h
${VST3_PLUGININTERFACES_BASE_PATH}/pluginbasefwd.h
${VST3_PLUGININTERFACES_BASE_PATH}/smartpointer.h
${VST3_PLUGININTERFACES_BASE_PATH}/typesizecheck.h
${VST3_PLUGININTERFACES_BASE_PATH}/ucolorspec.h
${VST3_PLUGININTERFACES_BASE_PATH}/ustring.cpp
${VST3_PLUGININTERFACES_BASE_PATH}/ustring.h
# sdk files.
# ${VST3_SRCPATH}/main/moduleinit.cpp
# ${VST3_SRCPATH}/main/moduleinit.h
${VST3_SRCPATH}/vst/utility/audiobuffers.h
${VST3_SRCPATH}/vst/utility/processcontextrequirements.h
${VST3_SRCPATH}/vst/utility/processdataslicer.h
${VST3_SRCPATH}/vst/utility/ringbuffer.h
${VST3_SRCPATH}/vst/utility/rttransfer.h
${VST3_SRCPATH}/vst/utility/sampleaccurate.h
${VST3_SRCPATH}/vst/utility/stringconvert.cpp
${VST3_SRCPATH}/vst/utility/stringconvert.h
${VST3_SRCPATH}/vst/utility/testing.cpp
${VST3_SRCPATH}/vst/utility/testing.h
${VST3_SRCPATH}/vst/utility/vst2persistence.h
${VST3_SRCPATH}/vst/utility/vst2persistence.cpp
${VST3_SRCPATH}/vst/vstaudioeffect.cpp
${VST3_SRCPATH}/vst/vstaudioeffect.h
${VST3_SRCPATH}/vst/vstbus.cpp
${VST3_SRCPATH}/vst/vstbus.h
${VST3_SRCPATH}/vst/vstbypassprocessor.h
${VST3_SRCPATH}/vst/vstcomponent.cpp
${VST3_SRCPATH}/vst/vstcomponent.h
${VST3_SRCPATH}/vst/vstcomponentbase.cpp
${VST3_SRCPATH}/vst/vstcomponentbase.h
${VST3_SRCPATH}/vst/vsteditcontroller.cpp
${VST3_SRCPATH}/vst/vsteditcontroller.h
${VST3_SRCPATH}/vst/vsteventshelper.h
${VST3_SRCPATH}/vst/vsthelpers.h
${VST3_SRCPATH}/vst/vstinitiids.cpp
${VST3_SRCPATH}/vst/vstnoteexpressiontypes.cpp
${VST3_SRCPATH}/vst/vstnoteexpressiontypes.h
${VST3_SRCPATH}/vst/vstparameters.cpp
${VST3_SRCPATH}/vst/vstparameters.h
${VST3_SRCPATH}/vst/vstrepresentation.cpp
${VST3_SRCPATH}/vst/vstrepresentation.h
# sdk_host.a files.
${VST3_SRCPATH}/vst/hosting/connectionproxy.cpp
${VST3_SRCPATH}/vst/hosting/connectionproxy.h
${VST3_SRCPATH}/vst/hosting/eventlist.cpp
${VST3_SRCPATH}/vst/hosting/eventlist.h
${VST3_SRCPATH}/vst/hosting/hostclasses.cpp
${VST3_SRCPATH}/vst/hosting/hostclasses.h
${VST3_SRCPATH}/vst/hosting/module.cpp
${VST3_SRCPATH}/vst/hosting/module.h
${VST3_SRCPATH}/vst/hosting/parameterchanges.cpp
${VST3_SRCPATH}/vst/hosting/parameterchanges.h
${VST3_SRCPATH}/vst/hosting/pluginterfacesupport.cpp
${VST3_SRCPATH}/vst/hosting/pluginterfacesupport.h
${VST3_SRCPATH}/vst/hosting/plugprovider.cpp
${VST3_SRCPATH}/vst/hosting/plugprovider.h
${VST3_SRCPATH}/vst/hosting/processdata.cpp
${VST3_SRCPATH}/vst/hosting/processdata.h
${VST3_SRCPATH}/vst/utility/optional.h
${VST3_SRCPATH}/vst/utility/stringconvert.cpp
${VST3_SRCPATH}/vst/utility/stringconvert.h
${VST3_SRCPATH}/vst/utility/uid.h
${VST3_SRCPATH}/vst/vstinitiids.cpp
${VST3_SRCPATH}/vst/hosting/module_linux.cpp
# source/audiohost.cpp
# source/audiohost.h
# source/media/audioclient.cpp
# ource/media/audioclient.h
# source/media/imediaserver.h
# source/media/iparameterclient.h
# #source/media/jack/jackclient.cpp
# source/media/miditovst.h
# source/platform/appinit.h
# source/usediids.cpp
)
add_library(vst3_lib STATIC ${vst3_sources})
target_include_directories(vst3_lib PRIVATE ${VST3_INCLUDES}
)
add_executable(vst3test Vst3test.cpp
MemDebug.cpp
MemDebug.hpp
Vst3SdkRepro.cpp
${VST3_FILES}
#vst3/Vst3PresetFile.hpp Vst3PresetFile.cpp
asan_options.cpp
)
target_include_directories(vst3test PRIVATE ${PIPEDAL_INCLUDES}
)
target_link_libraries(vst3test PRIVATE ${PIPEDAL_LIBS}
vst3_lib dl pthread
)
endif(ENABLE_VST3)
# Build machine tests. Run tests that are not dependent on hardware. # Build machine tests. Run tests that are not dependent on hardware.
add_test(NAME BuildTest COMMAND pipedaltest "[Build]") add_test(NAME BuildTest COMMAND pipedaltest "[Build]")
+11 -3
View File
@@ -23,11 +23,12 @@
namespace pipedal { namespace pipedal {
class RealtimeParameterRequest; class RealtimeParameterRequest;
class RealtimeRingBufferWriter;
class IEffect { class IEffect {
public: public:
virtual ~IEffect() {} virtual ~IEffect() {}
virtual long GetInstanceId() const = 0; virtual uint64_t GetInstanceId() const = 0;
virtual int GetControlIndex(const std::string&symbol) const = 0; virtual int GetControlIndex(const std::string&symbol) const = 0;
virtual void SetControl(int index, float value) = 0; virtual void SetControl(int index, float value) = 0;
virtual float GetControlValue(int index) const = 0; virtual float GetControlValue(int index) const = 0;
@@ -35,6 +36,7 @@ namespace pipedal {
virtual float GetOutputControlValue(int controlIndex) const = 0; virtual float GetOutputControlValue(int controlIndex) const = 0;
virtual int GetNumberOfInputAudioPorts() const = 0; virtual int GetNumberOfInputAudioPorts() const = 0;
virtual int GetNumberOfOutputAudioPorts() const = 0; virtual int GetNumberOfOutputAudioPorts() const = 0;
virtual float *GetAudioInputBuffer(int index) const = 0; virtual float *GetAudioInputBuffer(int index) const = 0;
@@ -43,13 +45,19 @@ namespace pipedal {
virtual void RequestParameter(LV2_URID uridUri) = 0; virtual void RequestParameter(LV2_URID uridUri) = 0;
virtual void GatherParameter(RealtimeParameterRequest*pRequest) = 0; virtual void GatherParameter(RealtimeParameterRequest*pRequest) = 0;
virtual std::string AtomToJson(uint8_t*pAtom) = 0; //virtual std::string AtomToJson(uint8_t*pAtom) = 0;
virtual std::string GetAtomObjectType(uint8_t*pData) = 0; //virtual std::string GetAtomObjectType(uint8_t*pData) = 0;
virtual void SetAudioInputBuffer(int index, float *buffer) = 0;
virtual void SetAudioOutputBuffer(int index, float*buffer) = 0; virtual void SetAudioOutputBuffer(int index, float*buffer) = 0;
virtual void Activate() = 0; virtual void Activate() = 0;
virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter) = 0;
virtual void Deactivate() = 0; virtual void Deactivate() = 0;
virtual bool IsVst3() const = 0;
}; };
} //namespace } //namespace
+239 -5
View File
@@ -106,6 +106,73 @@ static std::string GetGovernor()
class JackHostImpl : public JackHost, private AudioDriverHost class JackHostImpl : public JackHost, private AudioDriverHost
{ {
private: private:
IHost *pHost = nullptr;
class Uris
{
public:
Uris(IHost *pHost)
{
atom_Blank = pHost->GetLv2Urid(LV2_ATOM__Blank);
atom_Path = pHost->GetLv2Urid(LV2_ATOM__Path);
atom_float = pHost->GetLv2Urid(LV2_ATOM__Float);
atom_Double = pHost->GetLv2Urid(LV2_ATOM__Double);
atom_Int = pHost->GetLv2Urid(LV2_ATOM__Int);
atom_Long = pHost->GetLv2Urid(LV2_ATOM__Long);
atom_Bool = pHost->GetLv2Urid(LV2_ATOM__Bool);
atom_String = pHost->GetLv2Urid(LV2_ATOM__String);
atom_Vector = pHost->GetLv2Urid(LV2_ATOM__Vector);
atom_Object = pHost->GetLv2Urid(LV2_ATOM__Object);
atom_Sequence = pHost->GetLv2Urid(LV2_ATOM__Sequence);
atom_Chunk = pHost->GetLv2Urid(LV2_ATOM__Chunk);
atom_URID = pHost->GetLv2Urid(LV2_ATOM__URID);
atom_eventTransfer = pHost->GetLv2Urid(LV2_ATOM__eventTransfer);
patch_Get = pHost->GetLv2Urid(LV2_PATCH__Get);
patch_Set = pHost->GetLv2Urid(LV2_PATCH__Set);
patch_Put = pHost->GetLv2Urid(LV2_PATCH__Put);
patch_body = pHost->GetLv2Urid(LV2_PATCH__body);
patch_subject = pHost->GetLv2Urid(LV2_PATCH__subject);
patch_property = pHost->GetLv2Urid(LV2_PATCH__property);
//patch_accept = pHost->GetLv2Urid(LV2_PATCH__accept);
patch_value = pHost->GetLv2Urid(LV2_PATCH__value);
unitsFrame = pHost->GetLv2Urid(LV2_UNITS__frame);
}
//LV2_URID patch_accept;
LV2_URID unitsFrame;
LV2_URID pluginUri;
LV2_URID atom_Blank;
LV2_URID atom_Bool;
LV2_URID atom_float;
LV2_URID atom_Double;
LV2_URID atom_Int;
LV2_URID atom_Long;
LV2_URID atom_String;
LV2_URID atom_Object;
LV2_URID atom_Vector;
LV2_URID atom_Path;
LV2_URID atom_Sequence;
LV2_URID atom_Chunk;
LV2_URID atom_URID;
LV2_URID atom_eventTransfer;
LV2_URID midi_Event;
LV2_URID patch_Get;
LV2_URID patch_Set;
LV2_URID patch_Put;
LV2_URID patch_body;
LV2_URID patch_subject;
LV2_URID patch_property;
LV2_URID patch_value;
LV2_URID param_uiState;
};
Uris uris;
AudioDriver*audioDriver = nullptr; AudioDriver*audioDriver = nullptr;
inherit_priority_recursive_mutex mutex; inherit_priority_recursive_mutex mutex;
@@ -146,6 +213,30 @@ private:
std::atomic<std::chrono::system_clock::time_point> lastUnderrunTime = std::atomic<std::chrono::system_clock::time_point> lastUnderrunTime =
std::chrono::system_clock::from_time_t(0); std::chrono::system_clock::from_time_t(0);
std::string GetAtomObjectType(uint8_t*pData)
{
LV2_Atom_Object *pAtom = (LV2_Atom_Object*)pData;
if (pAtom->atom.type != uris.atom_Object)
{
throw std::invalid_argument("Not an Lv2 Object");
}
return pHost->Lv2UriudToString(pAtom->body.otype);
}
void WriteAtom(json_writer &writer, LV2_Atom*pAtom);
std::string AtomToJson(uint8_t*pData)
{
std::stringstream s;
json_writer writer(s);
LV2_Atom *pAtom = (LV2_Atom*)pData;
WriteAtom(writer,pAtom);
return s.str();
}
virtual void OnUnderrun() virtual void OnUnderrun()
{ {
++this->underruns; ++this->underruns;
@@ -582,7 +673,6 @@ private:
} }
} }
public: public:
JackHostImpl(IHost *pHost) JackHostImpl(IHost *pHost)
@@ -592,7 +682,9 @@ public:
realtimeWriter(&this->outputRingBuffer), realtimeWriter(&this->outputRingBuffer),
hostReader(&this->outputRingBuffer), hostReader(&this->outputRingBuffer),
hostWriter(&this->inputRingBuffer), hostWriter(&this->inputRingBuffer),
eventBufferUrids(pHost) eventBufferUrids(pHost),
pHost(pHost),
uris(pHost)
{ {
#if JACK_HOST #if JACK_HOST
@@ -758,7 +850,7 @@ public:
} }
else else
{ {
pRequest->jsonResponse = pEffect->AtomToJson(pRequest->response); pRequest->jsonResponse = AtomToJson(pRequest->response);
} }
} }
pRequest->onJackRequestComplete(pRequest); pRequest->onJackRequestComplete(pRequest);
@@ -802,8 +894,8 @@ public:
IEffect *pEffect = currentPedalBoard->GetEffect(instanceId); IEffect *pEffect = currentPedalBoard->GetEffect(instanceId);
if (pEffect != nullptr &&this->pNotifyCallbacks && listenForAtomOutput) if (pEffect != nullptr &&this->pNotifyCallbacks && listenForAtomOutput)
{ {
std::string atomType = pEffect->GetAtomObjectType(&atomBuffer[0]); std::string atomType = GetAtomObjectType(&atomBuffer[0]);
auto json = pEffect->AtomToJson(&(atomBuffer[0])); auto json = AtomToJson(&(atomBuffer[0]));
this->pNotifyCallbacks->OnNotifyAtomOutput(instanceId,atomType,json); this->pNotifyCallbacks->OnNotifyAtomOutput(instanceId,atomType,json);
} }
} }
@@ -1275,6 +1367,148 @@ public:
} }
}; };
static std::string UriToFieldName(const std::string&uri){
int pos;
for (pos = uri.length(); pos >= 0; --pos)
{
char c = uri[pos];
if (c == '#' || c == '/' || c == ':')
{
break;
}
}
return uri.substr(pos+1);
}
void JackHostImpl::WriteAtom(json_writer &writer, LV2_Atom*pAtom)
{
if (pAtom->type == uris.atom_Blank)
{
writer.write_raw("null");
}
else if (pAtom->type == uris.atom_float)
{
writer.write(
((LV2_Atom_Float*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Int)
{
writer.write(
((LV2_Atom_Int*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Long)
{
writer.write(
((LV2_Atom_Long*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Double)
{
writer.write(
((LV2_Atom_Double*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Bool)
{
writer.write(
((LV2_Atom_Bool*)pAtom)->body
);
} else if (pAtom->type == uris.atom_String)
{
const char *p = (((const char*) pAtom) + sizeof(LV2_Atom_String));
writer.write(
p
);
} else if (pAtom->type == uris.atom_Vector)
{
LV2_Atom_Vector *pVector = (LV2_Atom_Vector*)pAtom;
writer.start_array();
{
size_t n = (pAtom->size-sizeof(pVector->body))/pVector->body.child_size;
char *pItems = ((char*)pAtom) + sizeof(LV2_Atom_Vector);
if (pVector->body.child_type == uris.atom_float)
{
float *p = (float*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Int)
{
int32_t *p = (int32_t*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Long)
{
int64_t *p = (int64_t*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Double)
{
double *p = (double*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Bool)
{
bool *p = (bool*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
}
}
writer.end_array();
} else if (pAtom->type == uris.atom_Object)
{
writer.start_object();
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)pAtom;
bool firstMember = true;
if (obj->body.id != 0)
{
std::string id = pHost->Lv2UriudToString(obj->body.id);
writer.write_member("id",id.c_str());
firstMember = false;
}
if (obj->body.otype != 0)
{
std::string type = pHost->Lv2UriudToString(obj->body.otype);
writer.write_member("lv2Type",type.c_str());
if (!firstMember)
{
writer.write_raw(",");
}
firstMember = false;
}
LV2_ATOM_OBJECT_FOREACH (obj, prop) {
if (!firstMember) {
writer.write_raw(",");
}
firstMember = false;
std::string key = pHost->Lv2UriudToString(prop->key);
key = UriToFieldName(key);
writer.write(key);
writer.write_raw(": ");
LV2_Atom *value = &(prop->value);
WriteAtom(writer,value);
}
writer.end_object();
}
}
JackHost *JackHost::CreateInstance(IHost *pHost) JackHost *JackHost::CreateInstance(IHost *pHost)
{ {
+74
View File
@@ -0,0 +1,74 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "LiteralVersion.hpp"
#include <sstream>
#include <cctype>
using namespace pipedal;
LiteralVersion::LiteralVersion()
{
}
LiteralVersion::LiteralVersion(const std::string &version)
{
Parse(version);
}
bool LiteralVersion::Parse(const std::string &version)
{
int ix = 0;
while (ix < version.length())
{
char c = version[ix];
int n = 0;
if (std::isdigit(c))
{
while (std::isdigit(c))
{
c = version[ix++];
n = c -'0' + n;
}
this->versionNumbers.push_back(n);
if (version[ix] == '.')
{
++ix;
}
} else {
std::stringstream t;
while (ix < version.length())
{
c = version[ix++];
t << c;
}
suffix = t.str();
}
}
return true;
}
+45
View File
@@ -0,0 +1,45 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <vector>
#include <string>
namespace pipedal {
class LiteralVersion {
public:
LiteralVersion();
LiteralVersion(const std::string&version);
bool Parse(const std::string&version);
const std::vector<int> &getVersionNumbers() const { return versionNumbers; }
const std::string&getSuffix() const { return suffix; }
int compare(const LiteralVersion&other);
private:
std::vector<int> versionNumbers;
std::string suffix;
};
};
+4 -163
View File
@@ -216,7 +216,7 @@ void Lv2Effect::SetAudioInputBuffer(int index, float *buffer)
void Lv2Effect::SetAudioInputBuffer(float *left) void Lv2Effect::SetAudioInputBuffer(float *left)
{ {
if (GetNumberOfInputs() > 1) if (GetNumberOfInputAudioPorts() > 1)
{ {
SetAudioInputBuffer(0, left); SetAudioInputBuffer(0, left);
SetAudioInputBuffer(1, left); SetAudioInputBuffer(1, left);
@@ -229,7 +229,7 @@ void Lv2Effect::SetAudioInputBuffer(float *left)
void Lv2Effect::SetAudioInputBuffers(float *left, float *right) void Lv2Effect::SetAudioInputBuffers(float *left, float *right)
{ {
if (GetNumberOfInputs() == 1) if (GetNumberOfInputAudioPorts() == 1)
{ {
SetAudioInputBuffer(0, left); SetAudioInputBuffer(0, left);
} }
@@ -343,7 +343,7 @@ static inline void CopyBuffer(float *input, float *output, uint32_t frames)
} }
} }
void Lv2Effect::Run(uint32_t samples,uint64_t instanceId, RealtimeRingBufferWriter *realtimeRingBufferWriter) void Lv2Effect::Run(uint32_t samples,RealtimeRingBufferWriter *realtimeRingBufferWriter)
{ {
// close off the atom input frame. // close off the atom input frame.
if (this->inputAtomBuffers.size() != 0) if (this->inputAtomBuffers.size() != 0)
@@ -441,7 +441,7 @@ void Lv2Effect::Run(uint32_t samples,uint64_t instanceId, RealtimeRingBufferWrit
} }
} }
RelayOutputMessages(instanceId,realtimeRingBufferWriter); RelayOutputMessages(this->instanceId,realtimeRingBufferWriter);
} }
LV2_Worker_Status Lv2Effect::worker_schedule_fn(LV2_Worker_Schedule_Handle handle, LV2_Worker_Status Lv2Effect::worker_schedule_fn(LV2_Worker_Schedule_Handle handle,
@@ -600,164 +600,5 @@ void Lv2Effect::GatherParameter(RealtimeParameterRequest *pRequest)
} }
} }
std::string Lv2Effect::GetAtomObjectType(uint8_t*pData)
{
LV2_Atom_Object *pAtom = (LV2_Atom_Object*)pData;
if (pAtom->atom.type != uris.atom_Object)
{
throw std::invalid_argument("Not an Lv2 Object");
}
return pHost->Lv2UriudToString(pAtom->body.otype);
}
std::string Lv2Effect::AtomToJson(uint8_t*pData)
{
std::stringstream s;
json_writer writer(s);
LV2_Atom *pAtom = (LV2_Atom*)pData;
WriteAtom(writer,pAtom);
return s.str();
}
static std::string UriToFieldName(const std::string&uri){
int pos;
for (pos = uri.length(); pos >= 0; --pos)
{
char c = uri[pos];
if (c == '#' || c == '/' || c == ':')
{
break;
}
}
return uri.substr(pos+1);
}
void Lv2Effect::WriteAtom(json_writer &writer, LV2_Atom*pAtom)
{
if (pAtom->type == uris.atom_Blank)
{
writer.write_raw("null");
}
else if (pAtom->type == uris.atom_float)
{
writer.write(
((LV2_Atom_Float*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Int)
{
writer.write(
((LV2_Atom_Int*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Long)
{
writer.write(
((LV2_Atom_Long*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Double)
{
writer.write(
((LV2_Atom_Double*)pAtom)->body
);
} else if (pAtom->type == uris.atom_Bool)
{
writer.write(
((LV2_Atom_Bool*)pAtom)->body
);
} else if (pAtom->type == uris.atom_String)
{
const char *p = (((const char*) pAtom) + sizeof(LV2_Atom_String));
writer.write(
p
);
} else if (pAtom->type == uris.atom_Vector)
{
LV2_Atom_Vector *pVector = (LV2_Atom_Vector*)pAtom;
writer.start_array();
{
size_t n = (pAtom->size-sizeof(pVector->body))/pVector->body.child_size;
char *pItems = ((char*)pAtom) + sizeof(LV2_Atom_Vector);
if (pVector->body.child_type == uris.atom_float)
{
float *p = (float*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Int)
{
int32_t *p = (int32_t*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Long)
{
int64_t *p = (int64_t*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Double)
{
double *p = (double*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
} else if (pVector->body.child_type == uris.atom_Bool)
{
bool *p = (bool*)pItems;
for (size_t i = 0; i < n; ++i)
{
if (i != 0) writer.write_raw(",");
writer.write(*p++);
}
}
}
writer.end_array();
} else if (pAtom->type == uris.atom_Object)
{
writer.start_object();
const LV2_Atom_Object *obj = (const LV2_Atom_Object *)pAtom;
bool firstMember = true;
if (obj->body.id != 0)
{
std::string id = pHost->Lv2UriudToString(obj->body.id);
writer.write_member("id",id.c_str());
firstMember = false;
}
if (obj->body.otype != 0)
{
std::string type = pHost->Lv2UriudToString(obj->body.otype);
writer.write_member("lv2Type",type.c_str());
if (!firstMember)
{
writer.write_raw(",");
}
firstMember = false;
}
LV2_ATOM_OBJECT_FOREACH (obj, prop) {
if (!firstMember) {
writer.write_raw(",");
}
firstMember = false;
std::string key = pHost->Lv2UriudToString(prop->key);
key = UriToFieldName(key);
writer.write(key);
writer.write_raw(": ");
LV2_Atom *value = &(prop->value);
WriteAtom(writer,value);
}
writer.end_object();
}
}
+20 -35
View File
@@ -20,7 +20,7 @@
#pragma once #pragma once
#include "Lv2Host.hpp" #include "PiPedalHost.hpp"
#include "PedalBoard.hpp" #include "PedalBoard.hpp"
#include <lilv/lilv.h> #include <lilv/lilv.h>
#include "BufferPool.hpp" #include "BufferPool.hpp"
@@ -82,7 +82,6 @@ namespace pipedal
LV2_Atom_Forge outputForgeRt; LV2_Atom_Forge outputForgeRt;
void WriteAtom(json_writer &writer, LV2_Atom*pAtom);
class Uris class Uris
{ {
@@ -167,9 +166,10 @@ namespace pipedal
void BypassTo(float value); void BypassTo(float value);
virtual void RequestParameter(LV2_URID uridUri); virtual void RequestParameter(LV2_URID uridUri);
virtual void GatherParameter(RealtimeParameterRequest*pRequest); virtual void GatherParameter(RealtimeParameterRequest*pRequest);
virtual bool IsVst3() const { return false; }
virtual void RelayOutputMessages(uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter); virtual void RelayOutputMessages(uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter);
virtual uint8_t*GetAtomInputBuffer() { virtual uint8_t*GetAtomInputBuffer() {
@@ -182,10 +182,6 @@ namespace pipedal
return (uint8_t*)this->outputAtomBuffers[0]; return (uint8_t*)this->outputAtomBuffers[0];
} }
virtual std::string AtomToJson(uint8_t*pAtom);
virtual std::string GetAtomObjectType(uint8_t*pData);
public: public:
@@ -196,29 +192,30 @@ namespace pipedal
~Lv2Effect(); ~Lv2Effect();
virtual void ResetAtomBuffers(); virtual void ResetAtomBuffers();
virtual long GetInstanceId() const { return instanceId; } virtual uint64_t GetInstanceId() const { return instanceId; }
virtual int GetNumberOfInputAudioPorts() const { return inputAudioPortIndices.size(); } virtual int GetNumberOfInputAudioPorts() const { return inputAudioPortIndices.size(); }
virtual int GetNumberOfOutputAudioPorts() const { return outputAudioPortIndices.size(); } virtual int GetNumberOfOutputAudioPorts() const { return outputAudioPortIndices.size(); }
int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); } virtual int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); }
int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); } virtual int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); }
int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); } virtual int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); }
int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); } virtual int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); }
virtual void SetAudioInputBuffer(int index, float *buffer); virtual void SetAudioInputBuffer(int index, float *buffer);
float *GetAudioInputBuffer(int index) const { return this->inputAudioBuffers[index]; } virtual float *GetAudioInputBuffer(int index) const { return this->inputAudioBuffers[index]; }
void SetAudioInputBuffer(float *buffer); virtual void SetAudioInputBuffer(float *buffer);
void SetAudioInputBuffers(float *left, float *right); virtual void SetAudioInputBuffers(float *left, float *right);
virtual void SetAudioOutputBuffer(int index, float *buffer); virtual void SetAudioOutputBuffer(int index, float *buffer);
float *GetAudioOutputBuffer(int index) const { return this->outputAudioBuffers[index]; } virtual float *GetAudioOutputBuffer(int index) const { return this->outputAudioBuffers[index]; }
void SetAtomInputBuffer(int index, void *buffer); virtual void SetAtomInputBuffer(int index, void *buffer) { this->inputAtomBuffers[index] = (char*)buffer;}
void *GetAtomInputBuffer(int index) const { return this->inputAtomBuffers[index]; } virtual void *GetAtomInputBuffer(int index) const { return this->inputAtomBuffers[index]; }
void SetAtomOutputBuffer(int index, void *buffer); virtual void SetAtomOutputBuffer(int index, void *buffer) { this->outputAtomBuffers[index] = (char*)buffer; }
void *GetAtomOutputBuffer(int index) const { return this->outputAtomBuffers[index]; } virtual void *GetAtomOutputBuffer(int index) const { return this->outputAtomBuffers[index]; }
virtual int GetControlIndex(const std::string &symbol) const; virtual int GetControlIndex(const std::string &symbol) const;
@@ -254,22 +251,10 @@ namespace pipedal
} }
} }
int GetNumberOfInputs() const
{
return this->inputAudioPortIndices.size();
}
int GetNumberOfOutputs() const
{
return this->outputAudioPortIndices.size();
}
int GetNumberOfMidiInputs() const
{
return this->inputMidiPortIndices.size();
}
void Activate(); virtual void Activate();
void Run(uint32_t samples, uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter); virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter);
void Deactivate(); virtual void Deactivate();
}; };
} // namespace } // namespace
+1 -1
View File
@@ -19,7 +19,7 @@
#include "pch.h" #include "pch.h"
#include "Lv2EventBufferWriter.hpp" #include "Lv2EventBufferWriter.hpp"
#include "Lv2Host.hpp" #include "PiPedalHost.hpp"
using namespace pipedal; using namespace pipedal;
+3 -3
View File
@@ -24,17 +24,17 @@
#include <string> #include <string>
#include "Lv2Host.hpp" #include "PiPedalHost.hpp"
#include "MemDebug.hpp" #include "MemDebug.hpp"
using namespace pipedal; using namespace pipedal;
TEST_CASE( "Lv2Host memory leak", "[lv2host_leak][Build][Dev]" ) { TEST_CASE( "PiPedalHost memory leak", "[lv2host_leak][Build][Dev]" ) {
MemStats initialMemory = GetMemStats(); MemStats initialMemory = GetMemStats();
{ {
Lv2Host host; PiPedalHost host;
host.Load("/usr/lib/lv2:/usr/local/lib/lv2:/usr/modep/lv2"); host.Load("/usr/lib/lv2:/usr/local/lib/lv2:/usr/modep/lv2");
} }
+5 -4
View File
@@ -21,6 +21,7 @@
#include "Lv2PedalBoard.hpp" #include "Lv2PedalBoard.hpp"
#include "Lv2Effect.hpp" #include "Lv2Effect.hpp"
#include "SplitEffect.hpp" #include "SplitEffect.hpp"
#include "RingBufferReader.hpp" #include "RingBufferReader.hpp"
#include "VuUpdate.hpp" #include "VuUpdate.hpp"
@@ -57,7 +58,7 @@ int Lv2PedalBoard::GetControlIndex(uint64_t instanceId, const std::string &symbo
return -1; return -1;
} }
std::vector<float *> Lv2PedalBoard::PrepareItems( std::vector<float *> Lv2PedalBoard::PrepareItems(
const std::vector<PedalBoardItem> &items, std::vector<PedalBoardItem> &items,
std::vector<float *> inputBuffers) std::vector<float *> inputBuffers)
{ {
for (int i = 0; i < items.size(); ++i) for (int i = 0; i < items.size(); ++i)
@@ -142,9 +143,9 @@ std::vector<float *> Lv2PedalBoard::PrepareItems(
} }
this->processActions.push_back( this->processActions.push_back(
[pLv2Effect,this,instanceId](uint32_t frames) [pLv2Effect,this](uint32_t frames)
{ {
pLv2Effect->Run(frames,instanceId,this->ringBufferWriter); pLv2Effect->Run(frames,this->ringBufferWriter);
}); });
} }
} }
@@ -197,7 +198,7 @@ std::vector<float *> Lv2PedalBoard::PrepareItems(
return inputBuffers; return inputBuffers;
} }
void Lv2PedalBoard::Prepare(IHost *pHost, const PedalBoard &pedalBoard) void Lv2PedalBoard::Prepare(IHost *pHost, PedalBoard &pedalBoard)
{ {
this->pHost = pHost; this->pHost = pHost;
+5 -3
View File
@@ -19,7 +19,7 @@
#pragma once #pragma once
#include "PedalBoard.hpp" #include "PedalBoard.hpp"
#include "Lv2Host.hpp" #include "PiPedalHost.hpp"
#include "Lv2Effect.hpp" #include "Lv2Effect.hpp"
#include "BufferPool.hpp" #include "BufferPool.hpp"
#include <functional> #include <functional>
@@ -81,7 +81,7 @@ class Lv2PedalBoard {
std::vector<float*> PrepareItems( std::vector<float*> PrepareItems(
const std::vector<PedalBoardItem> & items, std::vector<PedalBoardItem> & items,
std::vector<float*> inputBuffers std::vector<float*> inputBuffers
); );
@@ -95,7 +95,9 @@ public:
Lv2PedalBoard() { } Lv2PedalBoard() { }
~Lv2PedalBoard() { } ~Lv2PedalBoard() { }
void Prepare(IHost *pHost,const PedalBoard&pedalBoard); void Prepare(IHost *pHost,PedalBoard&pedalBoard);
std::vector<IEffect* > GetEffects() { return realtimeEffects; }
int GetIndexOfInstanceId(uint64_t instanceId) { int GetIndexOfInstanceId(uint64_t instanceId) {
for (int i = 0; i < this->realtimeEffects.size(); ++i) for (int i = 0; i < this->realtimeEffects.size(); ++i)
+2
View File
@@ -82,10 +82,12 @@ class PedalBoardItem: public JsonWritable {
std::vector<PedalBoardItem> topChain_; std::vector<PedalBoardItem> topChain_;
std::vector<PedalBoardItem> bottomChain_; std::vector<PedalBoardItem> bottomChain_;
std::vector<MidiBinding> midiBindings_; std::vector<MidiBinding> midiBindings_;
std::string vstState_;
public: public:
ControlValue*GetControlValue(const std::string&symbol); ControlValue*GetControlValue(const std::string&symbol);
GETTER_SETTER(instanceId) GETTER_SETTER(instanceId)
GETTER_SETTER_REF(uri) GETTER_SETTER_REF(uri)
GETTER_SETTER_REF(vstState);
GETTER_SETTER_REF(pluginName) GETTER_SETTER_REF(pluginName)
GETTER_SETTER(isEnabled) GETTER_SETTER(isEnabled)
GETTER_SETTER_VEC(controlValues) GETTER_SETTER_VEC(controlValues)
+1
View File
@@ -36,4 +36,5 @@ JSON_MAP_REFERENCE(PiPedalConfiguration, logHttpRequests)
JSON_MAP_REFERENCE(PiPedalConfiguration, maxUploadSize) JSON_MAP_REFERENCE(PiPedalConfiguration, maxUploadSize)
JSON_MAP_REFERENCE(PiPedalConfiguration, accessPointGateway) JSON_MAP_REFERENCE(PiPedalConfiguration, accessPointGateway)
JSON_MAP_REFERENCE(PiPedalConfiguration, accessPointServerAddress) JSON_MAP_REFERENCE(PiPedalConfiguration, accessPointServerAddress)
JSON_MAP_REFERENCE(PiPedalConfiguration, isVst3Enabled)
JSON_MAP_END() JSON_MAP_END()
+2 -1
View File
@@ -47,9 +47,10 @@ private:
uint64_t maxUploadSize_ = 1024*1024; uint64_t maxUploadSize_ = 1024*1024;
std::string accessPointGateway_; std::string accessPointGateway_;
std::string accessPointServerAddress_; std::string accessPointServerAddress_;
bool isVst3Enabled_ = true;
public: public:
bool IsVst3Enabled() const { return isVst3Enabled_; }
std::filesystem::path GetConfigFilePath() const { std::filesystem::path GetConfigFilePath() const {
return docRoot_ / "config.jason"; return docRoot_ / "config.jason";
} }
+173 -112
View File
@@ -18,7 +18,7 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "pch.h" #include "pch.h"
#include "Lv2Host.hpp" #include "PiPedalHost.hpp"
#include <lilv/lilv.h> #include <lilv/lilv.h>
#include <stdexcept> #include <stdexcept>
#include <locale> #include <locale>
@@ -85,7 +85,7 @@ namespace pipedal
static const char *LV2_MIDI_EVENT = "http://lv2plug.in/ns/ext/midi#MidiEvent"; static const char *LV2_MIDI_EVENT = "http://lv2plug.in/ns/ext/midi#MidiEvent";
static const char *LV2_DESIGNATION = "http://lv2plug.in/ns/lv2core#Designation"; static const char *LV2_DESIGNATION = "http://lv2plug.in/ns/lv2core#Designation";
class Lv2Host::Urids class PiPedalHost::Urids
{ {
public: public:
Urids(MapFeature &mapFeature) Urids(MapFeature &mapFeature)
@@ -96,9 +96,17 @@ namespace pipedal
}; };
} }
void Lv2Host::LilvUris::Initialize(LilvWorld *pWorld) void PiPedalHost::SetConfiguration(const PiPedalConfiguration &configuration)
{ {
rdfsComment = lilv_new_uri(pWorld, Lv2Host::RDFS_COMMENT_URI); this->vst3CachePath =
std::filesystem::path(configuration.GetLocalStoragePath()) / "vst3cache.json";
this->vst3Enabled = configuration.IsVst3Enabled();
}
void PiPedalHost::LilvUris::Initialize(LilvWorld *pWorld)
{
rdfsComment = lilv_new_uri(pWorld, PiPedalHost::RDFS_COMMENT_URI);
logarithic_uri = lilv_new_uri(pWorld, LV2_PORT_LOGARITHMIC); logarithic_uri = lilv_new_uri(pWorld, LV2_PORT_LOGARITHMIC);
display_priority_uri = lilv_new_uri(pWorld, LV2_PORT_DISPLAY_PRIORITY); display_priority_uri = lilv_new_uri(pWorld, LV2_PORT_DISPLAY_PRIORITY);
range_steps_uri = lilv_new_uri(pWorld, LV2_PORT_RANGE_STEPS); range_steps_uri = lilv_new_uri(pWorld, LV2_PORT_RANGE_STEPS);
@@ -124,7 +132,7 @@ void Lv2Host::LilvUris::Initialize(LilvWorld *pWorld)
appliesTo = lilv_new_uri(pWorld, LV2_CORE__appliesTo); appliesTo = lilv_new_uri(pWorld, LV2_CORE__appliesTo);
} }
void Lv2Host::LilvUris::Free() void PiPedalHost::LilvUris::Free()
{ {
rdfsComment.Free(); rdfsComment.Free();
logarithic_uri.Free(); logarithic_uri.Free();
@@ -242,7 +250,7 @@ public:
} }
}; };
Lv2Host::Lv2Host() PiPedalHost::PiPedalHost()
{ {
pWorld = nullptr; pWorld = nullptr;
@@ -260,7 +268,7 @@ Lv2Host::Lv2Host()
this->urids = new Urids(mapFeature); this->urids = new Urids(mapFeature);
} }
void Lv2Host::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings) void PiPedalHost::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings)
{ {
this->sampleRate = configuration.GetSampleRate(); this->sampleRate = configuration.GetSampleRate();
if (configuration.isValid()) if (configuration.isValid())
@@ -272,7 +280,7 @@ void Lv2Host::OnConfigurationChanged(const JackConfiguration &configuration, con
} }
} }
Lv2Host::~Lv2Host() PiPedalHost::~PiPedalHost()
{ {
delete[] lv2Features; delete[] lv2Features;
lilvUris.Free(); lilvUris.Free();
@@ -280,7 +288,7 @@ Lv2Host::~Lv2Host()
delete urids; delete urids;
} }
void Lv2Host::free_world() void PiPedalHost::free_world()
{ {
if (pWorld) if (pWorld)
{ {
@@ -288,7 +296,7 @@ void Lv2Host::free_world()
pWorld = nullptr; pWorld = nullptr;
} }
} }
std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const std::string &uri) const std::shared_ptr<Lv2PluginClass> PiPedalHost::GetPluginClass(const std::string &uri) const
{ {
auto it = this->classesMap.find(uri); auto it = this->classesMap.find(uri);
if (it == this->classesMap.end()) if (it == this->classesMap.end())
@@ -296,7 +304,7 @@ std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const std::string &uri)
return (*it).second; return (*it).second;
} }
void Lv2Host::AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass) void PiPedalHost::AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass)
{ {
this->classesMap[pluginClass->uri()] = pluginClass; this->classesMap[pluginClass->uri()] = pluginClass;
for (int i = 0; i < pluginClass->children_.size(); ++i) for (int i = 0; i < pluginClass->children_.size(); ++i)
@@ -306,7 +314,7 @@ void Lv2Host::AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass)
child->parent_ = pluginClass.get(); child->parent_ = pluginClass.get();
} }
} }
void Lv2Host::LoadPluginClassesFromJson(std::filesystem::path jsonFile) void PiPedalHost::LoadPluginClassesFromJson(std::filesystem::path jsonFile)
{ {
std::ifstream f(jsonFile); std::ifstream f(jsonFile);
json_reader reader(f); json_reader reader(f);
@@ -320,7 +328,7 @@ void Lv2Host::LoadPluginClassesFromJson(std::filesystem::path jsonFile)
MAP_CHECK(); MAP_CHECK();
} }
std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const LilvPluginClass *pClass) std::shared_ptr<Lv2PluginClass> PiPedalHost::GetPluginClass(const LilvPluginClass *pClass)
{ {
MAP_CHECK(); MAP_CHECK();
@@ -328,7 +336,7 @@ std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const LilvPluginClass *p
std::shared_ptr<Lv2PluginClass> pResult = this->classesMap[uri]; std::shared_ptr<Lv2PluginClass> pResult = this->classesMap[uri];
return pResult; return pResult;
} }
std::shared_ptr<Lv2PluginClass> Lv2Host::MakePluginClass(const LilvPluginClass *pClass) std::shared_ptr<Lv2PluginClass> PiPedalHost::MakePluginClass(const LilvPluginClass *pClass)
{ {
std::string uri = nodeAsString(lilv_plugin_class_get_uri(pClass)); std::string uri = nodeAsString(lilv_plugin_class_get_uri(pClass));
auto t = this->classesMap.find(uri); auto t = this->classesMap.find(uri);
@@ -348,7 +356,7 @@ std::shared_ptr<Lv2PluginClass> Lv2Host::MakePluginClass(const LilvPluginClass *
return result; return result;
} }
void Lv2Host::LoadPluginClassesFromLilv() void PiPedalHost::LoadPluginClassesFromLilv()
{ {
const auto classes = lilv_world_get_plugin_classes(pWorld); const auto classes = lilv_world_get_plugin_classes(pWorld);
@@ -378,7 +386,7 @@ void Lv2Host::LoadPluginClassesFromLilv()
} }
} }
void Lv2Host::Load(const char *lv2Path) void PiPedalHost::Load(const char *lv2Path)
{ {
this->plugins_.clear(); this->plugins_.clear();
@@ -390,13 +398,14 @@ void Lv2Host::Load(const char *lv2Path)
free_world(); free_world();
Lv2Log::info("Scanning for LV2 Plugins");
setenv("LV2_PATH", lv2Path, true); setenv("LV2_PATH", lv2Path, true);
StdErrorCapture stdoutCapture; // captures lilv messages written to STDOUT. StdErrorCapture stdoutCapture; // captures lilv messages written to STDOUT.
pWorld = lilv_world_new(); pWorld = lilv_world_new();
{ {
lilv_world_load_all(pWorld); lilv_world_load_all(pWorld);
} }
lilvUris.Initialize(pWorld); lilvUris.Initialize(pWorld);
@@ -443,7 +452,7 @@ void Lv2Host::Load(const char *lv2Path)
} }
auto messages = stdoutCapture.GetOutputLines(); auto messages = stdoutCapture.GetOutputLines();
for (const std::string & s: messages) for (const std::string &s : messages)
{ {
Lv2Log::info("lilv: " + s); Lv2Log::info("lilv: " + s);
} }
@@ -491,8 +500,35 @@ void Lv2Host::Load(const char *lv2Path)
} }
} }
} }
}
; #if ENABLE_VST3
if (vst3Enabled)
{
Lv2Log::info("Scanning for VST3 Plugins");
this->vst3Host = Vst3Host::CreateInstance(
this->vst3CachePath);
this->vst3Host->RefreshPlugins();
const auto &vst3PluginList = this->vst3Host->getPluginList();
for (const auto &vst3Plugin : vst3PluginList)
{
// copy not move!
ui_plugins_.push_back(vst3Plugin->pluginInfo_);
}
auto ui_compare = [&collation](
Lv2PluginUiInfo &left,
Lv2PluginUiInfo &right)
{
const char *pb1 = left.name().c_str();
const char *pb2 = right.name().c_str();
return collation.compare(
pb1, pb1 + left.name().size(),
pb2, pb2 + right.name().size()) < 0;
};
std::sort(this->ui_plugins_.begin(), this->ui_plugins_.end(), ui_compare);
}
#endif
};
class NodesAutoFree class NodesAutoFree
{ {
@@ -532,10 +568,10 @@ static std::vector<std::string> nodeAsStringArray(const LilvNodes *nodes)
return result; return result;
} }
const char *Lv2Host::RDFS_COMMENT_URI = "http://www.w3.org/2000/01/rdf-schema#" const char *PiPedalHost::RDFS_COMMENT_URI = "http://www.w3.org/2000/01/rdf-schema#"
"comment"; "comment";
LilvNode *Lv2Host::get_comment(const std::string &uri) LilvNode *PiPedalHost::get_comment(const std::string &uri)
{ {
NodeAutoFree uriNode = lilv_new_uri(pWorld, uri.c_str()); NodeAutoFree uriNode = lilv_new_uri(pWorld, uri.c_str());
LilvNode *result = lilv_world_get(pWorld, uriNode, lilvUris.rdfsComment, nullptr); LilvNode *result = lilv_world_get(pWorld, uriNode, lilvUris.rdfsComment, nullptr);
@@ -547,7 +583,7 @@ static bool ports_sort_compare(std::shared_ptr<Lv2PortInfo> &p1, const std::shar
return p1->index() < p2->index(); return p1->index() < p2->index();
} }
bool Lv2PluginInfo::HasFactoryPresets(Lv2Host *lv2Host, const LilvPlugin *plugin) bool Lv2PluginInfo::HasFactoryPresets(PiPedalHost *lv2Host, const LilvPlugin *plugin)
{ {
NodesAutoFree nodes = lilv_plugin_get_related(plugin, lv2Host->lilvUris.pset_Preset); NodesAutoFree nodes = lilv_plugin_get_related(plugin, lv2Host->lilvUris.pset_Preset);
bool result = false; bool result = false;
@@ -559,7 +595,7 @@ bool Lv2PluginInfo::HasFactoryPresets(Lv2Host *lv2Host, const LilvPlugin *plugin
return result; return result;
} }
Lv2PluginInfo::Lv2PluginInfo(Lv2Host *lv2Host, const LilvPlugin *pPlugin) Lv2PluginInfo::Lv2PluginInfo(PiPedalHost *lv2Host, const LilvPlugin *pPlugin)
{ {
const LilvNode *pluginUri = lilv_plugin_get_uri(pPlugin); const LilvNode *pluginUri = lilv_plugin_get_uri(pPlugin);
@@ -664,7 +700,7 @@ Lv2PluginInfo::~Lv2PluginInfo()
{ {
} }
bool Lv2PortInfo::is_a(Lv2Host *lv2Plugins, const char *classUri) bool Lv2PortInfo::is_a(PiPedalHost *lv2Plugins, const char *classUri)
{ {
return classes_.is_a(lv2Plugins, classUri); return classes_.is_a(lv2Plugins, classUri);
} }
@@ -686,7 +722,7 @@ static bool scale_points_sort_compare(const Lv2ScalePoint &v1, const Lv2ScalePoi
{ {
return v1.value() < v2.value(); return v1.value() < v2.value();
} }
Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort *pPort) Lv2PortInfo::Lv2PortInfo(PiPedalHost *host, const LilvPlugin *plugin, const LilvPort *pPort)
{ {
auto pWorld = host->pWorld; auto pWorld = host->pWorld;
index_ = lilv_port_get_index(plugin, pPort); index_ = lilv_port_get_index(plugin, pPort);
@@ -785,7 +821,7 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort
NodeAutoFree unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri); NodeAutoFree unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri);
this->units_ = UriToUnits(nodeAsString(unitsValueUri)); this->units_ = UriToUnits(nodeAsString(unitsValueUri));
NodeAutoFree commentNode = lilv_port_get(plugin,pPort,host->lilvUris.rdfsComment); NodeAutoFree commentNode = lilv_port_get(plugin, pPort, host->lilvUris.rdfsComment);
this->comment_ = nodeAsString(commentNode); this->comment_ = nodeAsString(commentNode);
NodeAutoFree bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri); NodeAutoFree bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri);
@@ -800,7 +836,7 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort
((is_input_ || is_output_) && (is_audio_port_ || is_atom_port_ || is_cv_port_)); ((is_input_ || is_output_) && (is_audio_port_ || is_atom_port_ || is_cv_port_));
} }
Lv2PluginClasses Lv2Host::GetPluginPortClass(const LilvPlugin *lilvPlugin, const LilvPort *lilvPort) Lv2PluginClasses PiPedalHost::GetPluginPortClass(const LilvPlugin *lilvPlugin, const LilvPort *lilvPort)
{ {
std::vector<std::string> result; std::vector<std::string> result;
const LilvNodes *nodes = lilv_port_get_classes(lilvPlugin, lilvPort); const LilvNodes *nodes = lilv_port_get_classes(lilvPlugin, lilvPort);
@@ -832,7 +868,7 @@ bool Lv2PluginClass::is_a(const std::string &classUri) const
} }
return false; return false;
} }
bool Lv2PluginClasses::is_a(Lv2Host *lv2Plugins, const char *classUri) const bool Lv2PluginClasses::is_a(PiPedalHost *lv2Plugins, const char *classUri) const
{ {
std::string classUri_(classUri); std::string classUri_(classUri);
for (auto i : classes_) for (auto i : classes_)
@@ -845,7 +881,7 @@ bool Lv2PluginClasses::is_a(Lv2Host *lv2Plugins, const char *classUri) const
return false; return false;
} }
bool Lv2Host::is_a(const std::string &class_, const std::string &target_class) bool PiPedalHost::is_a(const std::string &class_, const std::string &target_class)
{ {
if (class_ == target_class) if (class_ == target_class)
return true; return true;
@@ -857,7 +893,7 @@ bool Lv2Host::is_a(const std::string &class_, const std::string &target_class)
return false; return false;
} }
Lv2PluginUiInfo::Lv2PluginUiInfo(Lv2Host *pHost, const Lv2PluginInfo *plugin) Lv2PluginUiInfo::Lv2PluginUiInfo(PiPedalHost *pHost, const Lv2PluginInfo *plugin)
: uri_(plugin->uri()), : uri_(plugin->uri()),
name_(plugin->name()), name_(plugin->name()),
author_name_(plugin->author_name()), author_name_(plugin->author_name()),
@@ -919,12 +955,12 @@ Lv2PluginUiInfo::Lv2PluginUiInfo(Lv2Host *pHost, const Lv2PluginInfo *plugin)
} }
} }
std::shared_ptr<Lv2PluginClass> Lv2Host::GetLv2PluginClass() const std::shared_ptr<Lv2PluginClass> PiPedalHost::GetLv2PluginClass() const
{ {
return this->GetPluginClass(LV2_PLUGIN); return this->GetPluginClass(LV2_PLUGIN);
} }
std::shared_ptr<Lv2PluginInfo> Lv2Host::GetPluginInfo(const std::string &uri) const std::shared_ptr<Lv2PluginInfo> PiPedalHost::GetPluginInfo(const std::string &uri) const
{ {
for (auto i = this->plugins_.begin(); i != this->plugins_.end(); ++i) for (auto i = this->plugins_.begin(); i != this->plugins_.end(); ++i)
{ {
@@ -936,7 +972,7 @@ std::shared_ptr<Lv2PluginInfo> Lv2Host::GetPluginInfo(const std::string &uri) co
return nullptr; return nullptr;
} }
Lv2PedalBoard *Lv2Host::CreateLv2PedalBoard(const PedalBoard &pedalBoard) Lv2PedalBoard *PiPedalHost::CreateLv2PedalBoard(PedalBoard &pedalBoard)
{ {
Lv2PedalBoard *pPedalBoard = new Lv2PedalBoard(); Lv2PedalBoard *pPedalBoard = new Lv2PedalBoard();
try try
@@ -953,18 +989,18 @@ Lv2PedalBoard *Lv2Host::CreateLv2PedalBoard(const PedalBoard &pedalBoard)
struct StateCallbackData struct StateCallbackData
{ {
Lv2Host *pHost; PiPedalHost *pHost;
std::vector<ControlValue> *pResult; std::vector<ControlValue> *pResult;
}; };
void Lv2Host::fn_LilvSetPortValueFunc(const char *port_symbol, void PiPedalHost::fn_LilvSetPortValueFunc(const char *port_symbol,
void *user_data, void *user_data,
const void *value, const void *value,
uint32_t size, uint32_t size,
uint32_t type) uint32_t type)
{ {
StateCallbackData *pData = (StateCallbackData *)user_data; StateCallbackData *pData = (StateCallbackData *)user_data;
Lv2Host *pHost = pData->pHost; PiPedalHost *pHost = pData->pHost;
std::vector<ControlValue> *pResult = pData->pResult; std::vector<ControlValue> *pResult = pData->pResult;
LV2_URID valueType = (LV2_URID)type; LV2_URID valueType = (LV2_URID)type;
if (valueType != pHost->urids->atom_Float) if (valueType != pHost->urids->atom_Float)
@@ -974,7 +1010,7 @@ void Lv2Host::fn_LilvSetPortValueFunc(const char *port_symbol,
pResult->push_back(ControlValue(port_symbol, *(float *)value)); pResult->push_back(ControlValue(port_symbol, *(float *)value));
} }
std::vector<ControlValue> Lv2Host::LoadFactoryPluginPreset( std::vector<ControlValue> PiPedalHost::LoadFactoryPluginPreset(
PedalBoardItem *pedalBoardItem, const std::string &presetUri) PedalBoardItem *pedalBoardItem, const std::string &presetUri)
{ {
@@ -1032,15 +1068,15 @@ std::vector<ControlValue> Lv2Host::LoadFactoryPluginPreset(
struct PresetCallbackState struct PresetCallbackState
{ {
Lv2Host *pHost; PiPedalHost *pHost;
std::map<std::string, float> *values; std::map<std::string, float> *values;
bool failed = false; bool failed = false;
}; };
void Lv2Host::PortValueCallback(const char *symbol, void *user_data, const void *value, uint32_t size, uint32_t type) void PiPedalHost::PortValueCallback(const char *symbol, void *user_data, const void *value, uint32_t size, uint32_t type)
{ {
PresetCallbackState *pState = static_cast<PresetCallbackState *>(user_data); PresetCallbackState *pState = static_cast<PresetCallbackState *>(user_data);
Lv2Host *pHost = pState->pHost; PiPedalHost *pHost = pState->pHost;
if (type == pHost->urids->atom_Float) if (type == pHost->urids->atom_Float)
{ {
@@ -1051,7 +1087,7 @@ void Lv2Host::PortValueCallback(const char *symbol, void *user_data, const void
pState->failed = true; pState->failed = true;
} }
} }
PluginPresets Lv2Host::GetFactoryPluginPresets(const std::string &pluginUri) PluginPresets PiPedalHost::GetFactoryPluginPresets(const std::string &pluginUri)
{ {
const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld); const LilvPlugins *plugins = lilv_world_get_all_plugins(this->pWorld);
@@ -1093,17 +1129,38 @@ PluginPresets Lv2Host::GetFactoryPluginPresets(const std::string &pluginUri)
return result; return result;
} }
Lv2Effect *Lv2Host::CreateEffect(const PedalBoardItem &pedalBoardItem) IEffect *PiPedalHost::CreateEffect(PedalBoardItem &pedalBoardItem)
{ {
if (pedalBoardItem.uri().starts_with("vst3:"))
{
#if ENABLE_VST3
try
{
Vst3Effect::Ptr vst3Plugin = this->vst3Host->CreatePlugin(pedalBoardItem, this);
return vst3Plugin.release();
}
catch (const std::exception &e)
{
Lv2Log::error(std::string("Failed to create VST3 plugin: ") + e.what());
throw;
}
#else
Lv2Log::error(std::string("VST3 support not enabled at compile time."));
throw PiPedalException("VST3 support not enabled at compile time, SEE ENABLE_VST3 in CMakeList.txt.");
auto info = this->GetPluginInfo(pedalBoardItem.uri()); #endif
if (!info) }
return nullptr; else
{
auto info = this->GetPluginInfo(pedalBoardItem.uri());
if (!info)
return nullptr;
return new Lv2Effect(this, info, pedalBoardItem); return new Lv2Effect(this, info, pedalBoardItem);
}
} }
Lv2PortGroup::Lv2PortGroup(Lv2Host *lv2Host, const std::string &groupUri) Lv2PortGroup::Lv2PortGroup(PiPedalHost *lv2Host, const std::string &groupUri)
{ {
LilvWorld *pWorld = lv2Host->pWorld; LilvWorld *pWorld = lv2Host->pWorld;
@@ -1131,47 +1188,45 @@ json_map::storage_type<Lv2ScalePoint> Lv2ScalePoint::jmap{{
json_map::reference("label", &Lv2ScalePoint::label_), json_map::reference("label", &Lv2ScalePoint::label_),
}}; }};
json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("index", &Lv2PortInfo::index_), json_map::reference("symbol", &Lv2PortInfo::symbol_),
json_map::reference("symbol", &Lv2PortInfo::symbol_), json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("index", &Lv2PortInfo::index_), json_map::reference("name", &Lv2PortInfo::name_),
json_map::reference("name", &Lv2PortInfo::name_),
json_map::reference("min_value", &Lv2PortInfo::min_value_), json_map::reference("min_value", &Lv2PortInfo::min_value_),
json_map::reference("max_value", &Lv2PortInfo::max_value_), json_map::reference("max_value", &Lv2PortInfo::max_value_),
json_map::reference("default_value", &Lv2PortInfo::default_value_), json_map::reference("default_value", &Lv2PortInfo::default_value_),
json_map::reference("classes", &Lv2PortInfo::classes_), json_map::reference("classes", &Lv2PortInfo::classes_),
json_map::reference("scale_points", &Lv2PortInfo::scale_points_), json_map::reference("scale_points", &Lv2PortInfo::scale_points_),
json_map::reference("is_input", &Lv2PortInfo::is_input_), json_map::reference("is_input", &Lv2PortInfo::is_input_),
json_map::reference("is_output", &Lv2PortInfo::is_output_), json_map::reference("is_output", &Lv2PortInfo::is_output_),
json_map::reference("is_control_port", &Lv2PortInfo::is_control_port_), json_map::reference("is_control_port", &Lv2PortInfo::is_control_port_),
json_map::reference("is_audio_port", &Lv2PortInfo::is_audio_port_), json_map::reference("is_audio_port", &Lv2PortInfo::is_audio_port_),
json_map::reference("is_atom_port", &Lv2PortInfo::is_audio_port_), json_map::reference("is_atom_port", &Lv2PortInfo::is_audio_port_),
json_map::reference("is_cv_port", &Lv2PortInfo::is_cv_port_), json_map::reference("is_cv_port", &Lv2PortInfo::is_cv_port_),
json_map::reference("is_valid", &Lv2PortInfo::is_valid_), json_map::reference("is_valid", &Lv2PortInfo::is_valid_),
json_map::reference("supports_midi", &Lv2PortInfo::supports_midi_), json_map::reference("supports_midi", &Lv2PortInfo::supports_midi_),
json_map::reference("supports_time_position", &Lv2PortInfo::supports_time_position_), json_map::reference("supports_time_position", &Lv2PortInfo::supports_time_position_),
json_map::reference("port_group", &Lv2PortInfo::port_group_), json_map::reference("port_group", &Lv2PortInfo::port_group_),
json_map::reference("is_logarithmic", &Lv2PortInfo::is_logarithmic_), json_map::reference("is_logarithmic", &Lv2PortInfo::is_logarithmic_),
MAP_REF(Lv2PortInfo, is_logarithmic), MAP_REF(Lv2PortInfo, is_logarithmic),
MAP_REF(Lv2PortInfo, display_priority), MAP_REF(Lv2PortInfo, display_priority),
MAP_REF(Lv2PortInfo, range_steps), MAP_REF(Lv2PortInfo, range_steps),
MAP_REF(Lv2PortInfo, trigger), MAP_REF(Lv2PortInfo, trigger),
MAP_REF(Lv2PortInfo, integer_property), MAP_REF(Lv2PortInfo, integer_property),
MAP_REF(Lv2PortInfo, enumeration_property), MAP_REF(Lv2PortInfo, enumeration_property),
MAP_REF(Lv2PortInfo, toggled_property), MAP_REF(Lv2PortInfo, toggled_property),
MAP_REF(Lv2PortInfo, not_on_gui), MAP_REF(Lv2PortInfo, not_on_gui),
MAP_REF(Lv2PortInfo, buffer_type), MAP_REF(Lv2PortInfo, buffer_type),
MAP_REF(Lv2PortInfo, port_group), MAP_REF(Lv2PortInfo, port_group),
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()), json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
MAP_REF(Lv2PortInfo, comment) MAP_REF(Lv2PortInfo, comment)}};
}};
json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{ json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{
MAP_REF(Lv2PortGroup, uri), MAP_REF(Lv2PortGroup, uri),
@@ -1197,21 +1252,19 @@ json_map::storage_type<Lv2PluginInfo> Lv2PluginInfo::jmap{{
json_map::reference("has_factory_presets", &Lv2PluginInfo::has_factory_presets_), json_map::reference("has_factory_presets", &Lv2PluginInfo::has_factory_presets_),
}}; }};
json_map::storage_type<Lv2PluginUiInfo> Lv2PluginUiInfo::jmap{{ json_map::storage_type<Lv2PluginClass> Lv2PluginClass::jmap{{
json_map::reference("uri", &Lv2PluginUiInfo::uri_), MAP_REF(Lv2PluginClass, uri),
json_map::reference("name", &Lv2PluginUiInfo::name_), MAP_REF(Lv2PluginClass, display_name),
json_map::enum_reference("plugin_type", &Lv2PluginUiInfo::plugin_type_, get_plugin_type_enum_converter()), MAP_REF(Lv2PluginClass, parent_uri),
json_map::reference("plugin_display_type", &Lv2PluginUiInfo::plugin_display_type_), json_map::enum_reference("plugin_type", &Lv2PluginClass::plugin_type_, get_plugin_type_enum_converter()),
json_map::reference("author_name", &Lv2PluginUiInfo::author_name_), MAP_REF(Lv2PluginClass, children),
json_map::reference("author_homepage", &Lv2PluginUiInfo::author_homepage_), }};
json_map::reference("audio_inputs", &Lv2PluginUiInfo::audio_inputs_),
json_map::reference("audio_outputs", &Lv2PluginUiInfo::audio_outputs_),
json_map::reference("description", &Lv2PluginUiInfo::description_),
json_map::reference("has_midi_input", &Lv2PluginUiInfo::has_midi_input_),
json_map::reference("has_midi_output", &Lv2PluginUiInfo::has_midi_output_),
json_map::reference("controls", &Lv2PluginUiInfo::controls_),
json_map::reference("port_groups", &Lv2PluginUiInfo::port_groups_),
json_map::storage_type<Lv2PluginUiPortGroup> Lv2PluginUiPortGroup::jmap{{
MAP_REF(Lv2PluginUiPortGroup, symbol),
MAP_REF(Lv2PluginUiPortGroup, name),
MAP_REF(Lv2PluginUiPortGroup, parent_group),
MAP_REF(Lv2PluginUiPortGroup, program_list_id),
}}; }};
json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{ json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{
@@ -1234,18 +1287,26 @@ json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{
json_map::enum_reference("units", &Lv2PluginUiControlPort::units_, get_units_enum_converter()), json_map::enum_reference("units", &Lv2PluginUiControlPort::units_, get_units_enum_converter()),
MAP_REF(Lv2PluginUiControlPort, comment), MAP_REF(Lv2PluginUiControlPort, comment),
MAP_REF(Lv2PluginUiControlPort, is_bypass),
MAP_REF(Lv2PluginUiControlPort, is_program_controller),
MAP_REF(Lv2PluginUiControlPort, custom_units),
}}; }};
json_map::storage_type<Lv2PluginClass> Lv2PluginClass::jmap{{ json_map::storage_type<Lv2PluginUiInfo> Lv2PluginUiInfo::jmap{{
MAP_REF(Lv2PluginClass, uri), json_map::reference("uri", &Lv2PluginUiInfo::uri_),
MAP_REF(Lv2PluginClass, display_name), json_map::reference("name", &Lv2PluginUiInfo::name_),
MAP_REF(Lv2PluginClass, parent_uri), json_map::enum_reference("plugin_type", &Lv2PluginUiInfo::plugin_type_, get_plugin_type_enum_converter()),
json_map::enum_reference("plugin_type", &Lv2PluginClass::plugin_type_, get_plugin_type_enum_converter()), json_map::reference("plugin_display_type", &Lv2PluginUiInfo::plugin_display_type_),
MAP_REF(Lv2PluginClass, children), json_map::reference("author_name", &Lv2PluginUiInfo::author_name_),
}}; json_map::reference("author_homepage", &Lv2PluginUiInfo::author_homepage_),
json_map::reference("audio_inputs", &Lv2PluginUiInfo::audio_inputs_),
json_map::reference("audio_outputs", &Lv2PluginUiInfo::audio_outputs_),
json_map::reference("description", &Lv2PluginUiInfo::description_),
json_map::reference("has_midi_input", &Lv2PluginUiInfo::has_midi_input_),
json_map::reference("has_midi_output", &Lv2PluginUiInfo::has_midi_output_),
json_map::reference("controls", &Lv2PluginUiInfo::controls_),
json_map::reference("port_groups", &Lv2PluginUiInfo::port_groups_),
json_map::reference("is_vst3", &Lv2PluginUiInfo::is_vst3_),
json_map::storage_type<Lv2PluginUiPortGroup> Lv2PluginUiPortGroup::jmap{{
MAP_REF(Lv2PluginUiPortGroup, symbol),
MAP_REF(Lv2PluginUiPortGroup, name),
}}; }};
+181 -138
View File
@@ -30,11 +30,15 @@
#include "OptionsFeature.hpp" #include "OptionsFeature.hpp"
#include <filesystem> #include <filesystem>
#include <cmath> #include <cmath>
#include <string>
#include "lv2.h" #include "lv2.h"
#include "Units.hpp" #include "Units.hpp"
#include "PluginPreset.hpp" #include "PluginPreset.hpp"
#include "IEffect.hpp"
#include "PiPedalConfiguration.hpp"
namespace pipedal { namespace pipedal {
@@ -42,19 +46,23 @@ namespace pipedal {
// forward declarations // forward declarations
class Lv2Effect; class Lv2Effect;
class Lv2PedalBoard; class Lv2PedalBoard;
class Lv2Host; class PiPedalHost;
class JackConfiguration; class JackConfiguration;
class JackChannelSelection; class JackChannelSelection;
#ifndef LV2_PROPERTY_GETSET
#define LV2_PROPERTY_GETSET(name) \ #define LV2_PROPERTY_GETSET(name) \
const decltype(name##_) & name() const { return name##_; }; \ const decltype(name##_) & name() const { return name##_; }; \
decltype(name##_) & name() { return name##_; }; \ decltype(name##_) & name() { return name##_; }; \
void name(const decltype(name##_) &value) { name##_ = value; }; void name(const decltype(name##_) &value) { name##_ = value; };
#endif
#ifndef LV2_PROPERTY_GETSET_SCALAR
#define LV2_PROPERTY_GETSET_SCALAR(name) \ #define LV2_PROPERTY_GETSET_SCALAR(name) \
decltype(name##_) name() const { return name##_;}; \ decltype(name##_) name() const { return name##_;}; \
void name(decltype(name##_) value) { name##_ = value; }; void name(decltype(name##_) value) { name##_ = value; };
#endif
@@ -62,7 +70,7 @@ class JackChannelSelection;
class Lv2PluginClass { class Lv2PluginClass {
public: public:
friend class Lv2Host; friend class PiPedalHost;
private: private:
Lv2PluginClass* parent_ = nullptr; // NOT SERIALIZED! Lv2PluginClass* parent_ = nullptr; // NOT SERIALIZED!
std::string parent_uri_; std::string parent_uri_;
@@ -72,7 +80,7 @@ private:
std::vector<std::shared_ptr<Lv2PluginClass>> children_; std::vector<std::shared_ptr<Lv2PluginClass>> children_;
friend class ::pipedal::Lv2Host; friend class ::pipedal::PiPedalHost;
// hide copy constructor. // hide copy constructor.
Lv2PluginClass(const Lv2PluginClass &other) Lv2PluginClass(const Lv2PluginClass &other)
{ {
@@ -135,7 +143,7 @@ public:
{ {
return classes_; return classes_;
} }
bool is_a(Lv2Host*lv2Plugins,const char*classUri) const; bool is_a(PiPedalHost*lv2Plugins,const char*classUri) const;
static json_map::storage_type<Lv2PluginClasses> jmap; static json_map::storage_type<Lv2PluginClasses> jmap;
@@ -171,7 +179,7 @@ enum class Lv2BufferType {
class Lv2PortInfo { class Lv2PortInfo {
public: public:
Lv2PortInfo(Lv2Host*lv2Host,const LilvPlugin*pPlugin,const LilvPort *pPort); Lv2PortInfo(PiPedalHost*lv2Host,const LilvPlugin*pPlugin,const LilvPort *pPort);
private: private:
friend class Lv2PluginInfo; friend class Lv2PluginInfo;
@@ -276,7 +284,7 @@ public:
public: public:
Lv2PortInfo() { } Lv2PortInfo() { }
~Lv2PortInfo() = default; ~Lv2PortInfo() = default;
bool is_a(Lv2Host*lv2Plugins,const char*classUri); bool is_a(PiPedalHost*lv2Plugins,const char*classUri);
static json_map::storage_type<Lv2PortInfo> jmap; static json_map::storage_type<Lv2PortInfo> jmap;
@@ -294,19 +302,19 @@ public:
LV2_PROPERTY_GETSET(name); LV2_PROPERTY_GETSET(name);
Lv2PortGroup() { } Lv2PortGroup() { }
Lv2PortGroup(Lv2Host*lv2Host,const std::string &groupUri); Lv2PortGroup(PiPedalHost*lv2Host,const std::string &groupUri);
static json_map::storage_type<Lv2PortGroup> jmap; static json_map::storage_type<Lv2PortGroup> jmap;
}; };
class Lv2PluginInfo { class Lv2PluginInfo {
private: private:
friend class Lv2Host; friend class PiPedalHost;
public: public:
Lv2PluginInfo(Lv2Host*lv2Host,const LilvPlugin*); Lv2PluginInfo(PiPedalHost*lv2Host,const LilvPlugin*);
Lv2PluginInfo() { } Lv2PluginInfo() { }
private: private:
bool HasFactoryPresets(Lv2Host*lv2Host, const LilvPlugin*plugin); bool HasFactoryPresets(PiPedalHost*lv2Host, const LilvPlugin*plugin);
std::string uri_; std::string uri_;
std::string name_; std::string name_;
std::string plugin_class_; std::string plugin_class_;
@@ -399,139 +407,151 @@ public:
}; };
class Lv2PluginUiPortGroup
class Lv2PluginUiControlPort {
public:
Lv2PluginUiControlPort() {
}
Lv2PluginUiControlPort(const Lv2PluginInfo *pPlugin,const Lv2PortInfo*pPort)
: symbol_(pPort->symbol())
, index_(pPort->index())
, name_(pPort->name())
, min_value_(pPort->min_value())
, max_value_(pPort->max_value())
, default_value_(pPort->default_value())
, range_steps_(pPort->range_steps())
, display_priority_(pPort->display_priority())
, is_logarithmic_(pPort->is_logarithmic())
, integer_property_(pPort->integer_property())
, enumeration_property_(pPort->enumeration_property())
, toggled_property_(pPort->toggled_property())
, not_on_gui_(pPort->not_on_gui())
, scale_points_(pPort->scale_points())
, comment_(pPort->comment())
, units_(pPort->units())
{ {
// Use symbols to index port groups, instead of uris. private:
// symbols are guaranteed to be unique. std::string symbol_;
auto &portGroup = pPort->port_group(); std::string name_;
for (int i = 0; i < pPlugin->port_groups().size(); ++i)
std::string parent_group_;
int32_t program_list_id_ = -1; // used by VST3.
public:
LV2_PROPERTY_GETSET(symbol)
LV2_PROPERTY_GETSET(name)
LV2_PROPERTY_GETSET(parent_group)
LV2_PROPERTY_GETSET_SCALAR(program_list_id)
public:
Lv2PluginUiPortGroup() {}
Lv2PluginUiPortGroup(Lv2PortGroup *pPortGroup)
: symbol_(pPortGroup->symbol()), name_(pPortGroup->name())
{
}
Lv2PluginUiPortGroup(
const std::string &symbol, const std::string &name,
const std::string &parent_group, int32_t programListId)
: symbol_(symbol),name_(name),parent_group_(parent_group),program_list_id_(programListId)
{ {
auto &p = pPlugin->port_groups()[i]; }
if (p->uri() == portGroup)
{
this->port_group_ = p->symbol();
break;
}
} public:
} static json_map::storage_type<Lv2PluginUiPortGroup> jmap;
private: };
std::string symbol_;
int index_;
std::string name_;
float min_value_ = 0, max_value_ = 1,default_value_ = 0;
int range_steps_ = 0;
int display_priority_ = -1;
bool is_logarithmic_ = false;
bool integer_property_ = false;
bool enumeration_property_ = false;
bool toggled_property_ = false;
bool not_on_gui_ = false;
std::vector<Lv2ScalePoint> scale_points_;
std::string port_group_;
Units units_ = Units::none;
std::string comment_;
public:
LV2_PROPERTY_GETSET(symbol);
LV2_PROPERTY_GETSET_SCALAR(index);
LV2_PROPERTY_GETSET(name);
LV2_PROPERTY_GETSET_SCALAR(min_value);
LV2_PROPERTY_GETSET_SCALAR(max_value);
LV2_PROPERTY_GETSET_SCALAR(default_value);
LV2_PROPERTY_GETSET_SCALAR(range_steps);
LV2_PROPERTY_GETSET_SCALAR(display_priority);
LV2_PROPERTY_GETSET_SCALAR(is_logarithmic);
LV2_PROPERTY_GETSET_SCALAR(integer_property);
LV2_PROPERTY_GETSET_SCALAR(enumeration_property);
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
LV2_PROPERTY_GETSET(scale_points);
LV2_PROPERTY_GETSET(units);
LV2_PROPERTY_GETSET(comment);
public: class Lv2PluginUiControlPort
static json_map::storage_type<Lv2PluginUiControlPort> jmap;
};
class Lv2PluginUiPortGroup {
private:
std::string symbol_;
std::string name_;
public:
Lv2PluginUiPortGroup() { }
Lv2PluginUiPortGroup(Lv2PortGroup *pPortGroup)
: symbol_(pPortGroup->symbol())
, name_(pPortGroup->name())
{ {
} public:
public: Lv2PluginUiControlPort()
static json_map::storage_type<Lv2PluginUiPortGroup> jmap; {
}
Lv2PluginUiControlPort(const Lv2PluginInfo *pPlugin, const Lv2PortInfo *pPort)
: symbol_(pPort->symbol()), index_(pPort->index()), name_(pPort->name()), min_value_(pPort->min_value()), max_value_(pPort->max_value()), default_value_(pPort->default_value()), range_steps_(pPort->range_steps()), display_priority_(pPort->display_priority()), is_logarithmic_(pPort->is_logarithmic()), integer_property_(pPort->integer_property()), enumeration_property_(pPort->enumeration_property()), toggled_property_(pPort->toggled_property()), not_on_gui_(pPort->not_on_gui()), scale_points_(pPort->scale_points()), comment_(pPort->comment()), units_(pPort->units())
{
// Use symbols to index port groups, instead of uris.
// symbols are guaranteed to be unique.
auto &portGroup = pPort->port_group();
for (int i = 0; i < pPlugin->port_groups().size(); ++i)
{
}; auto &p = pPlugin->port_groups()[i];
if (p->uri() == portGroup)
{
this->port_group_ = p->symbol();
break;
}
}
}
class Lv2PluginUiInfo { private:
public: std::string symbol_;
Lv2PluginUiInfo() { } int index_;
Lv2PluginUiInfo(Lv2Host *pPlugins,const Lv2PluginInfo *plugin); std::string name_;
float min_value_ = 0, max_value_ = 1, default_value_ = 0;
int range_steps_ = 0;
int display_priority_ = -1;
bool is_logarithmic_ = false;
bool integer_property_ = false;
bool enumeration_property_ = false;
bool toggled_property_ = false;
bool not_on_gui_ = false;
std::vector<Lv2ScalePoint> scale_points_;
std::string port_group_;
Units units_ = Units::none;
std::string comment_;
bool is_bypass_ = false;
bool is_program_controller_ = false;
std::string custom_units_;
private: public:
std::string uri_; LV2_PROPERTY_GETSET(symbol);
std::string name_; LV2_PROPERTY_GETSET_SCALAR(index);
std::string author_name_; LV2_PROPERTY_GETSET(name);
std::string author_homepage_; LV2_PROPERTY_GETSET(port_group);
PluginType plugin_type_; LV2_PROPERTY_GETSET_SCALAR(min_value);
std::string plugin_display_type_; LV2_PROPERTY_GETSET_SCALAR(max_value);
int audio_inputs_ = 0; LV2_PROPERTY_GETSET_SCALAR(default_value);
int audio_outputs_ = 0; LV2_PROPERTY_GETSET_SCALAR(range_steps);
int has_midi_input_ = false; LV2_PROPERTY_GETSET_SCALAR(display_priority);
int has_midi_output_ = false; LV2_PROPERTY_GETSET_SCALAR(is_logarithmic);
std::string description_; LV2_PROPERTY_GETSET_SCALAR(integer_property);
LV2_PROPERTY_GETSET_SCALAR(enumeration_property);
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
LV2_PROPERTY_GETSET(scale_points);
LV2_PROPERTY_GETSET(units);
LV2_PROPERTY_GETSET(comment);
LV2_PROPERTY_GETSET_SCALAR(is_bypass);
LV2_PROPERTY_GETSET_SCALAR(is_program_controller);
LV2_PROPERTY_GETSET(custom_units);
std::vector<Lv2PluginUiControlPort> controls_; public:
std::vector<Lv2PluginUiPortGroup> port_groups_; static json_map::storage_type<Lv2PluginUiControlPort> jmap;
};
public: class Lv2PluginUiInfo
LV2_PROPERTY_GETSET(uri) {
LV2_PROPERTY_GETSET(name) public:
LV2_PROPERTY_GETSET(author_name) Lv2PluginUiInfo() {}
LV2_PROPERTY_GETSET(author_homepage) Lv2PluginUiInfo(PiPedalHost *pPlugins, const Lv2PluginInfo *plugin);
LV2_PROPERTY_GETSET_SCALAR(plugin_type)
LV2_PROPERTY_GETSET(plugin_display_type) private:
LV2_PROPERTY_GETSET_SCALAR(audio_inputs) std::string uri_;
LV2_PROPERTY_GETSET_SCALAR(audio_outputs) std::string name_;
LV2_PROPERTY_GETSET_SCALAR(has_midi_input) std::string author_name_;
LV2_PROPERTY_GETSET_SCALAR(has_midi_output) std::string author_homepage_;
LV2_PROPERTY_GETSET_SCALAR(description) PluginType plugin_type_;
LV2_PROPERTY_GETSET(port_groups) std::string plugin_display_type_;
int audio_inputs_ = 0;
int audio_outputs_ = 0;
int has_midi_input_ = false;
int has_midi_output_ = false;
std::string description_;
bool is_vst3_ = false;
std::vector<Lv2PluginUiControlPort> controls_;
std::vector<Lv2PluginUiPortGroup> port_groups_;
public:
LV2_PROPERTY_GETSET(uri)
LV2_PROPERTY_GETSET(name)
LV2_PROPERTY_GETSET(author_name)
LV2_PROPERTY_GETSET(author_homepage)
LV2_PROPERTY_GETSET_SCALAR(plugin_type)
LV2_PROPERTY_GETSET(plugin_display_type)
LV2_PROPERTY_GETSET_SCALAR(audio_inputs)
LV2_PROPERTY_GETSET_SCALAR(audio_outputs)
LV2_PROPERTY_GETSET_SCALAR(has_midi_input)
LV2_PROPERTY_GETSET_SCALAR(has_midi_output)
LV2_PROPERTY_GETSET_SCALAR(description)
LV2_PROPERTY_GETSET(controls)
LV2_PROPERTY_GETSET(port_groups)
LV2_PROPERTY_GETSET_SCALAR(is_vst3)
static json_map::storage_type<Lv2PluginUiInfo> jmap;
};
static json_map::storage_type<Lv2PluginUiInfo> jmap;
};
class IHost { class IHost {
public: public:
@@ -552,7 +572,7 @@ public:
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string&uri) const = 0; virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string&uri) const = 0;
virtual Lv2Effect*CreateEffect(const PedalBoardItem &pedalBoard) = 0; virtual IEffect*CreateEffect(PedalBoardItem &pedalBoard) = 0;
}; };
@@ -583,8 +603,24 @@ public:
LilvNodePtr&operator=(LilvNode*node) { Free(); this->node = node; return *this;} LilvNodePtr&operator=(LilvNode*node) { Free(); this->node = node; return *this;}
}; };
class Lv2Host: private IHost { }
#if ENABLE_VST3
#include "vst3/Vst3Host.hpp"
#endif
namespace pipedal{
class PiPedalHost: private IHost {
private: private:
#if ENABLE_VST3
Vst3Host::Ptr vst3Host;
#endif
static const char *RDFS_COMMENT_URI; static const char *RDFS_COMMENT_URI;
class LilvUris { class LilvUris {
public: public:
@@ -619,6 +655,8 @@ private:
}; };
bool vst3Enabled = true;
LilvUris lilvUris; LilvUris lilvUris;
LilvNode *get_comment(const std::string &uri); LilvNode *get_comment(const std::string &uri);
@@ -631,6 +669,8 @@ private:
int numberOfAudioOutputChannels = 2; int numberOfAudioOutputChannels = 2;
double sampleRate = 0; double sampleRate = 0;
std::string vst3CachePath;
LV2_Feature*const*lv2Features = nullptr; LV2_Feature*const*lv2Features = nullptr;
MapFeature mapFeature; MapFeature mapFeature;
LogFeature logFeature; LogFeature logFeature;
@@ -678,17 +718,20 @@ private:
} }
static void PortValueCallback(const char*symbol,void*user_data,const void* value,uint32_t size, uint32_t type); static void PortValueCallback(const char*symbol,void*user_data,const void* value,uint32_t size, uint32_t type);
virtual Lv2Effect*CreateEffect(const PedalBoardItem &pedalBoardItem); virtual IEffect*CreateEffect(PedalBoardItem &pedalBoardItem);
void LoadPluginClassesFromLilv(); void LoadPluginClassesFromLilv();
void AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass); void AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass);
public: public:
Lv2Host(); PiPedalHost();
virtual ~Lv2Host();
void SetConfiguration(const PiPedalConfiguration&configuration);
virtual ~PiPedalHost();
IHost* asIHost() { return this;} IHost* asIHost() { return this;}
virtual Lv2PedalBoard * CreateLv2PedalBoard(const PedalBoard& pedalBoard); virtual Lv2PedalBoard * CreateLv2PedalBoard(PedalBoard& pedalBoard);
void setSampleRate(double sampleRate) void setSampleRate(double sampleRate)
{ {
@@ -719,7 +762,7 @@ public:
void LoadPluginClassesFromJson(std::filesystem::path jsonFile); void LoadPluginClassesFromJson(std::filesystem::path jsonFile);
void Load(const char*lv2Path = Lv2Host::DEFAULT_LV2_PATH); void Load(const char*lv2Path = PiPedalHost::DEFAULT_LV2_PATH);
virtual LV2_URID GetLv2Urid(const char*uri) { virtual LV2_URID GetLv2Urid(const char*uri) {
return this->mapFeature.GetUrid(uri); return this->mapFeature.GetUrid(uri);
@@ -736,5 +779,5 @@ public:
}; };
#undef LV2_PROPERTY_GETSET #undef LV2_PROPERTY_GETSET
#undef LV2_PROPERTY_GETSET_SCALAR
} // namespace pipedal. } // namespace pipedal.
+86 -24
View File
@@ -43,9 +43,18 @@ T &constMutex(const T &mutex)
return const_cast<T &>(mutex); return const_cast<T &>(mutex);
} }
std::string AtomToJson(int length, uint8_t *pData) static const char *hexChars = "0123456789ABCDEF";
static std::string BytesToHex(const std::vector<uint8_t> &bytes)
{ {
return ""; std::stringstream s;
for (size_t i = 0; i < bytes.size(); ++i)
{
uint8_t b = bytes[i];
s << hexChars[(b >> 4) & 0x0F] << hexChars[b & 0x0F];
}
return s.str();
} }
PiPedalModel::PiPedalModel() PiPedalModel::PiPedalModel()
@@ -119,6 +128,7 @@ PiPedalModel::~PiPedalModel()
void PiPedalModel::Init(const PiPedalConfiguration &configuration) void PiPedalModel::Init(const PiPedalConfiguration &configuration)
{ {
this->configuration = configuration; this->configuration = configuration;
lv2Host.SetConfiguration(configuration);
storage.SetConfigRoot(configuration.GetDocRoot()); storage.SetConfigRoot(configuration.GetDocRoot());
storage.SetDataRoot(configuration.GetLocalStoragePath()); storage.SetDataRoot(configuration.GetLocalStoragePath());
storage.Initialize(); storage.Initialize();
@@ -282,7 +292,17 @@ void PiPedalModel::RemoveNotificationSubsription(IPiPedalModelSubscriber *pSubsc
void PiPedalModel::PreviewControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value) void PiPedalModel::PreviewControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value)
{ {
jackHost->SetControlValue(pedalItemId, symbol, value); IEffect* effect = lv2PedalBoard->GetEffect(pedalItemId);
if (effect->IsVst3())
{
int index = lv2PedalBoard->GetControlIndex(pedalItemId, symbol);
if (index != -1)
{
effect->SetControl(index,value);;
}
} else {
jackHost->SetControlValue(pedalItemId, symbol, value);
}
} }
void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value) void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value)
@@ -291,6 +311,8 @@ void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
PreviewControl(clientId, pedalItemId, symbol, value); PreviewControl(clientId, pedalItemId, symbol, value);
this->pedalBoard.SetControlValue(pedalItemId, symbol, value); this->pedalBoard.SetControlValue(pedalItemId, symbol, value);
IEffect *pEffect = this->lv2PedalBoard->GetEffect(pedalItemId);
{ {
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us) // take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
@@ -300,9 +322,11 @@ void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::
t[i] = this->subscribers[i]; t[i] = this->subscribers[i];
} }
size_t n = this->subscribers.size(); size_t n = this->subscribers.size();
for (size_t i = 0; i < n; ++i)
{ {
t[i]->OnControlChanged(clientId, pedalItemId, symbol, value); for (size_t i = 0; i < n; ++i)
{
t[i]->OnControlChanged(clientId, pedalItemId, symbol, value);
}
} }
delete[] t; delete[] t;
@@ -379,6 +403,23 @@ void PiPedalModel::SetPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
this->SetPresetChanged(clientId, true); this->SetPresetChanged(clientId, true);
} }
} }
void PiPedalModel::UpdateCurrentPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
{
{
std::lock_guard<std::recursive_mutex> guard(mutex);
// update vst3 presets if neccessary.
// the pedalboard must be a manipualted instance of the current Lv2Pedalboard.
UpdateVst3Settings(pedalBoard);
this->pedalBoard = pedalBoard;
UpdateDefaults(&this->pedalBoard);
this->FirePedalBoardChanged(clientId);
this->SetPresetChanged(clientId, true);
}
}
void PiPedalModel::SetPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled) void PiPedalModel::SetPedalBoardItemEnable(int64_t clientId, int64_t pedalItemId, bool enabled)
{ {
@@ -475,10 +516,35 @@ void PiPedalModel::FirePluginPresetsChanged(const std::string &pluginUri)
} }
} }
void PiPedalModel::UpdateVst3Settings(PedalBoard &pedalBoard)
{
// get the vst3 state bundle from lv2Pedalboard for the current pedalBoard.
#if ENABLE_VST3
PedalBoard pb;
for (IEffect *effect : lv2PedalBoard->GetEffects())
{
if (effect->IsVst3())
{
PedalBoardItem *item = pedalBoard.GetItem(effect->GetInstanceId());
if (item)
{
Vst3Effect *vst3Effect = (Vst3Effect *)effect;
std::vector<uint8_t> state;
if (vst3Effect->GetState(&state))
{
item->vstState(BytesToHex(state));
}
}
}
}
#endif
}
void PiPedalModel::SaveCurrentPreset(int64_t clientId) void PiPedalModel::SaveCurrentPreset(int64_t clientId)
{ {
std::lock_guard<std::recursive_mutex> guard{mutex}; std::lock_guard<std::recursive_mutex> guard{mutex};
UpdateVst3Settings(this->pedalBoard);
storage.SaveCurrentPreset(this->pedalBoard); storage.SaveCurrentPreset(this->pedalBoard);
this->SetPresetChanged(clientId, false); this->SetPresetChanged(clientId, false);
} }
@@ -517,6 +583,7 @@ int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &n
std::lock_guard<std::recursive_mutex> guard{mutex}; std::lock_guard<std::recursive_mutex> guard{mutex};
auto pedalboard = this->pedalBoard; auto pedalboard = this->pedalBoard;
UpdateVst3Settings(pedalBoard);
pedalboard.name(name); pedalboard.name(name);
int64_t result = storage.SaveCurrentPresetAs(pedalboard, name, saveAfterInstanceId); int64_t result = storage.SaveCurrentPresetAs(pedalboard, name, saveAfterInstanceId);
FirePresetsChanged(clientId); FirePresetsChanged(clientId);
@@ -697,14 +764,13 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
} }
} }
static std::string GetP2pdName() static std::string GetP2pdName()
{ {
std::string name = "/etc/pipedal/config/pipedal_p2pd.conf"; std::string name = "/etc/pipedal/config/pipedal_p2pd.conf";
std::string result; std::string result;
if (ConfigUtil::GetConfigLine(name,"p2p_device_name",&result)) if (ConfigUtil::GetConfigLine(name, "p2p_device_name", &result))
{ {
return result; return result;
} }
@@ -718,17 +784,15 @@ void PiPedalModel::UpdateDnsSd()
ServiceConfiguration deviceIdFile; ServiceConfiguration deviceIdFile;
deviceIdFile.Load(); deviceIdFile.Load();
std::string p2pdName = GetP2pdName(); std::string p2pdName = GetP2pdName();
if (p2pdName != "") { if (p2pdName != "")
{
deviceIdFile.deviceName = p2pdName; deviceIdFile.deviceName = p2pdName;
} }
if (deviceIdFile.deviceName != "" && deviceIdFile.uuid != "") if (deviceIdFile.deviceName != "" && deviceIdFile.uuid != "")
{ {
avahiService.Announce(webPort, deviceIdFile.deviceName, deviceIdFile.uuid, "pipedal"); avahiService.Announce(webPort, deviceIdFile.deviceName, deviceIdFile.uuid, "pipedal");
} }
else else
{ {
@@ -820,16 +884,16 @@ void PiPedalModel::RestartAudio()
this->jackHost->Close(); this->jackHost->Close();
} }
// restarting is a bit dodgy. It was impossible with Jack, but // restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack. // now very plausible with the ALSA audio stack.
// Still bugs wrt/ restarting the circular buffers for the audio thread. // Still bugs wrt/ restarting the circular buffers for the audio thread.
// for the meantime, just rely on the fact that the admin service will restart // for the meantime, just rely on the fact that the admin service will restart
// the process. // the process.
//... //...
// do a complete reload. // do a complete reload.
this->jackHost->SetPedalBoard(nullptr); this->jackHost->SetPedalBoard(nullptr);
@@ -839,14 +903,14 @@ void PiPedalModel::RestartAudio()
{ {
JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration); JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration);
selection = selection.RemoveInvalidChannels(jackConfiguration); selection = selection.RemoveInvalidChannels(jackConfiguration);
}
} else { else
{
jackConfiguration.SetErrorStatus("Error"); jackConfiguration.SetErrorStatus("Error");
} }
FireJackConfigurationChanged(jackConfiguration); FireJackConfigurationChanged(jackConfiguration);
if (!jackServerSettings.IsValid() || !jackConfiguration.isValid()) if (!jackServerSettings.IsValid() || !jackConfiguration.isValid())
{ {
Lv2Log::error("Audio configuration not valid."); Lv2Log::error("Audio configuration not valid.");
@@ -1223,12 +1287,11 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
} }
delete[] t; delete[] t;
#if ALSA_HOST #if ALSA_HOST
storage.SetJackServerSettings(jackServerSettings); storage.SetJackServerSettings(jackServerSettings);
FireJackConfigurationChanged(this->jackConfiguration); FireJackConfigurationChanged(this->jackConfiguration);
guard.unlock(); guard.unlock();
RestartAudio(); RestartAudio();
@@ -1280,7 +1343,6 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
#endif #endif
} }
}); });
} }
#endif #endif
} }
+7 -3
View File
@@ -19,7 +19,7 @@
#pragma once #pragma once
#include <mutex> #include <mutex>
#include "Lv2Host.hpp" #include "PiPedalHost.hpp"
#include "GovernorSettings.hpp" #include "GovernorSettings.hpp"
#include "PedalBoard.hpp" #include "PedalBoard.hpp"
#include "Storage.hpp" #include "Storage.hpp"
@@ -52,6 +52,7 @@ public:
virtual int64_t GetClientId() = 0; virtual int64_t GetClientId() = 0;
virtual void OnItemEnabledChanged(int64_t clientId, int64_t pedalItemId, bool enabled) = 0; virtual void OnItemEnabledChanged(int64_t clientId, int64_t pedalItemId, bool enabled) = 0;
virtual void OnControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value) = 0; virtual void OnControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value) = 0;
virtual void OnVst3ControlChanged(int64_t clientId, int64_t pedalItemId, const std::string &symbol, float value, const std::string&state) = 0;
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard) = 0; virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard) = 0;
virtual void OnPresetsChanged(int64_t clientId, const PresetIndex&presets) = 0; virtual void OnPresetsChanged(int64_t clientId, const PresetIndex&presets) = 0;
virtual void OnPluginPresetsChanged(const std::string&pluginUri) = 0; virtual void OnPluginPresetsChanged(const std::string&pluginUri) = 0;
@@ -104,7 +105,7 @@ private:
std::vector<AtomOutputListener> atomOutputListeners; std::vector<AtomOutputListener> atomOutputListeners;
JackServerSettings jackServerSettings; JackServerSettings jackServerSettings;
Lv2Host lv2Host; PiPedalHost lv2Host;
PedalBoard pedalBoard; PedalBoard pedalBoard;
Storage storage; Storage storage;
bool hasPresetChanged = false; bool hasPresetChanged = false;
@@ -155,6 +156,7 @@ private: // IJackHostCallbacks
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl); virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl);
virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson); virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson);
void UpdateVst3Settings(PedalBoard&pedalBoard);
PiPedalConfiguration configuration; PiPedalConfiguration configuration;
public: public:
@@ -173,7 +175,7 @@ public:
void LoadLv2PluginInfo(); void LoadLv2PluginInfo();
void Load(); void Load();
const Lv2Host& GetLv2Host() const { return lv2Host; } const PiPedalHost& GetLv2Host() const { return lv2Host; }
PedalBoard GetCurrentPedalBoardCopy() PedalBoard GetCurrentPedalBoardCopy()
{ {
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -191,6 +193,8 @@ public:
void SetControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value); void SetControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void PreviewControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value); void PreviewControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void SetPedalBoard(int64_t clientId,PedalBoard &pedalBoard); void SetPedalBoard(int64_t clientId,PedalBoard &pedalBoard);
void UpdateCurrentPedalBoard(int64_t clientId, PedalBoard &pedalBoard);
void GetPresets(PresetIndex*pResult); void GetPresets(PresetIndex*pResult);
PedalBoard GetPreset(int64_t instanceId); PedalBoard GetPreset(int64_t instanceId);
+40 -9
View File
@@ -277,18 +277,18 @@ JSON_MAP_REFERENCE(PedalBoardItemEnabledBody, instanceId)
JSON_MAP_REFERENCE(PedalBoardItemEnabledBody, enabled) JSON_MAP_REFERENCE(PedalBoardItemEnabledBody, enabled)
JSON_MAP_END() JSON_MAP_END()
class SetCurrentPedalBoardBody class UpdateCurrentPedalBoardBody
{ {
public: public:
int64_t clientId_ = -1; int64_t clientId_ = -1;
PedalBoard pedalBoard_; PedalBoard pedalBoard_;
DECLARE_JSON_MAP(SetCurrentPedalBoardBody); DECLARE_JSON_MAP(UpdateCurrentPedalBoardBody);
}; };
JSON_MAP_BEGIN(SetCurrentPedalBoardBody) JSON_MAP_BEGIN(UpdateCurrentPedalBoardBody)
JSON_MAP_REFERENCE(SetCurrentPedalBoardBody, clientId) JSON_MAP_REFERENCE(UpdateCurrentPedalBoardBody, clientId)
JSON_MAP_REFERENCE(SetCurrentPedalBoardBody, pedalBoard) JSON_MAP_REFERENCE(UpdateCurrentPedalBoardBody, pedalBoard)
JSON_MAP_END() JSON_MAP_END()
class ChannelSelectionChangedBody class ChannelSelectionChangedBody
@@ -335,6 +335,26 @@ JSON_MAP_REFERENCE(ControlChangedBody, symbol)
JSON_MAP_REFERENCE(ControlChangedBody, value) JSON_MAP_REFERENCE(ControlChangedBody, value)
JSON_MAP_END() JSON_MAP_END()
class Vst3ControlChangedBody
{
public:
int64_t clientId_;
int64_t instanceId_;
std::string symbol_;
float value_;
std::string state_;
DECLARE_JSON_MAP(Vst3ControlChangedBody);
};
JSON_MAP_BEGIN(Vst3ControlChangedBody)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, clientId)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, instanceId)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, symbol)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, value)
JSON_MAP_REFERENCE(Vst3ControlChangedBody, state)
JSON_MAP_END()
class PiPedalSocketHandler : public SocketHandler, public IPiPedalModelSubscriber class PiPedalSocketHandler : public SocketHandler, public IPiPedalModelSubscriber
{ {
private: private:
@@ -940,13 +960,13 @@ public:
pReader->read(&body); pReader->read(&body);
model.SetPedalBoardItemEnable(body.clientId_, body.instanceId_, body.enabled_); model.SetPedalBoardItemEnable(body.clientId_, body.instanceId_, body.enabled_);
} }
else if (message == "setCurrentPedalBoard") else if (message == "updateCurrentPedalBoard")
{ {
{ {
SetCurrentPedalBoardBody body; UpdateCurrentPedalBoardBody body;
pReader->read(&body); pReader->read(&body);
this->model.SetPedalBoard(body.clientId_, body.pedalBoard_); this->model.UpdateCurrentPedalBoard(body.clientId_, body.pedalBoard_);
} }
} }
else if (message == "currentPedalBoard") else if (message == "currentPedalBoard")
@@ -1377,6 +1397,17 @@ public:
} }
} }
virtual void OnVst3ControlChanged(int64_t clientId, int64_t instanceId, const std::string &key, float value, const std::string &state)
{
Vst3ControlChangedBody body;
body.clientId_ = clientId;
body.instanceId_ = instanceId;
body.symbol_ = key;
body.value_ = value;
body.state_ = state;
Send("onVst3ControlChanged", body);
}
virtual void OnControlChanged(int64_t clientId, int64_t instanceId, const std::string &key, float value) virtual void OnControlChanged(int64_t clientId, int64_t instanceId, const std::string &key, float value)
{ {
ControlChangedBody body; ControlChangedBody body;
@@ -1544,7 +1575,7 @@ public:
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard) virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard)
{ {
SetCurrentPedalBoardBody body; UpdateCurrentPedalBoardBody body;
body.clientId_ = clientId; body.clientId_ = clientId;
body.pedalBoard_ = pedalBoard; body.pedalBoard_ = pedalBoard;
Send("onPedalBoardChanged", body); Send("onPedalBoardChanged", body);
+69
View File
@@ -0,0 +1,69 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "ss.hpp"
#include "Lv2Log.hpp"
namespace pipedal
{
constexpr int RT_THREAD_PRIORITY = 80;
/**
* @brief RAII Priority boost to avoid Rt-thread priority inversion
* Boosts current thread to realtime priority for the duration of the current scope.
* Do this before acquiring a mutex that the RT thread might block on.
*
*/
class RtInversionGuard
{
private:
int currentScheduler;
struct sched_param currentPriority;
;
public:
RtInversionGuard()
{
currentScheduler = sched_getscheduler(0);
sched_getparam(0, &currentPriority);
struct sched_param param;
memset(&param, 0, sizeof(param));
param.sched_priority = RT_THREAD_PRIORITY;
int result = sched_setscheduler(0, SCHED_RR, &param);
if (result != 0)
{
Lv2Log::error(SS("Failed to set ALSA AudioThread priority. (" << strerror(result) << ")"));
}
}
~RtInversionGuard()
{
sched_setscheduler(0, currentScheduler, &currentPriority);
}
};
}
+1 -1
View File
@@ -20,7 +20,7 @@
#include "pch.h" #include "pch.h"
#include "SplitEffect.hpp" #include "SplitEffect.hpp"
#include "Lv2Host.hpp" #include "PiPedalHost.hpp"
#include "PedalBoard.hpp" #include "PedalBoard.hpp"
using namespace pipedal; using namespace pipedal;
+16 -1
View File
@@ -22,6 +22,7 @@
#include "IEffect.hpp" #include "IEffect.hpp"
#include "PiPedalException.hpp" #include "PiPedalException.hpp"
#include "PiPedalMath.hpp" #include "PiPedalMath.hpp"
#include <assert.h>
namespace pipedal namespace pipedal
{ {
@@ -60,6 +61,18 @@ namespace pipedal
class SplitEffect : public IEffect class SplitEffect : public IEffect
{ {
private: private:
virtual void SetAudioInputBuffer(int index, float *buffer)
{
assert(false); // not used.
}
virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter)
{
assert(false); // not used.
}
const double MIX_TRANSITION_TIME_S = 0.1; const double MIX_TRANSITION_TIME_S = 0.1;
double sampleRate; double sampleRate;
std::vector<float *> inputs; std::vector<float *> inputs;
@@ -283,6 +296,8 @@ namespace pipedal
virtual std::string AtomToJson(uint8_t *pAtom) { return ""; } virtual std::string AtomToJson(uint8_t *pAtom) { return ""; }
virtual std::string GetAtomObjectType(uint8_t*pData) { return "not implemented";} virtual std::string GetAtomObjectType(uint8_t*pData) { return "not implemented";}
virtual bool IsVst3() const { return false; }
public: public:
SplitEffect( SplitEffect(
uint64_t instanceId, uint64_t instanceId,
@@ -297,7 +312,7 @@ namespace pipedal
} }
virtual long GetInstanceId() const virtual uint64_t GetInstanceId() const
{ {
return instanceId; return instanceId;
} }
+1
View File
@@ -44,6 +44,7 @@ enum class Units {
pc, pc,
s, s,
semitone12TET, semitone12TET,
custom
}; };
+885
View File
@@ -0,0 +1,885 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "ss.hpp"
#include <assert.h>
#include "PiPedalHost.hpp"
#include "vst3/Vst3Host.hpp"
#include "Lv2Log.hpp"
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <filesystem>
#include "RingBufferReader.hpp"
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "public.sdk/source/common/memorystream.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/iparameterclient.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h"
#include <array>
#include <sstream>
#include "LiteralVersion.hpp"
#include "Vst3MidiToEvent.hpp"
#include "vst3/Vst3EffectImpl.hpp"
#include "PiPedalHost.hpp"
using namespace pipedal;
std::unique_ptr<Vst3Effect> Vst3Effect::CreateInstance(uint64_t instanceId, const Vst3PluginInfo &info, IHost *pHost)
{
std::unique_ptr<Vst3EffectImpl> result = std::make_unique<Vst3EffectImpl>();
result->Load(instanceId, info, pHost);
return std::move(result);
}
static double NaNGuard(double value)
{
if (std::isnan(value)) return 0;
if (std::isinf(value)) return 0;
return value;
}
static uint32_t GetRefCount(FUnknown *p)
{
p->addRef();
return p->release();
}
//------------------------------------------------------------------------
// From Vst2Wrapper
static MidiCCMapping initMidiCtrlerAssignment(IComponent *component, IMidiMapping *midiMapping)
{
MidiCCMapping midiCCMapping{};
if (!midiMapping || !component)
return midiCCMapping;
int32 busses = std::min<int32>(component->getBusCount(kEvent, kInput), kMaxMidiMappingBusses);
if (midiCCMapping[0][0].empty())
{
for (int32 b = 0; b < busses; b++)
for (int32 i = 0; i < kMaxMidiChannels; i++)
midiCCMapping[b][i].resize(Vst::kCountCtrlNumber);
}
ParamID paramID;
for (int32 b = 0; b < busses; b++)
{
for (int16 ch = 0; ch < kMaxMidiChannels; ch++)
{
for (int32 i = 0; i < Vst::kCountCtrlNumber; i++)
{
paramID = kNoParamId;
if (midiMapping->getMidiControllerAssignment(b, ch, (CtrlNumber)i, paramID) ==
kResultTrue)
{
// TODO check if tag is associated to a parameter
midiCCMapping[b][ch][i] = paramID;
}
else
midiCCMapping[b][ch][i] = kNoParamId;
}
}
}
return midiCCMapping;
}
//------------------------------------------------------------------------
static void assignBusBuffers(const IAudioClient::Buffers &buffers, HostProcessData &processData,
bool unassign = false)
{
// Set outputs
auto bufferIndex = 0;
for (auto busIndex = 0; busIndex < processData.numOutputs; busIndex++)
{
auto channelCount = processData.outputs[busIndex].numChannels;
for (auto chanIndex = 0; chanIndex < channelCount; chanIndex++)
{
if (bufferIndex < buffers.numOutputs)
{
processData.setChannelBuffer(BusDirections::kOutput, busIndex, chanIndex,
unassign ? nullptr : buffers.outputs[bufferIndex]);
bufferIndex++;
}
}
}
// Set inputs
bufferIndex = 0;
for (auto busIndex = 0; busIndex < processData.numInputs; busIndex++)
{
auto channelCount = processData.inputs[busIndex].numChannels;
for (auto chanIndex = 0; chanIndex < channelCount; chanIndex++)
{
if (bufferIndex < buffers.numInputs)
{
processData.setChannelBuffer(BusDirections::kInput, busIndex, chanIndex,
unassign ? nullptr : buffers.inputs[bufferIndex]);
bufferIndex++;
}
}
}
}
//------------------------------------------------------------------------
static void unassignBusBuffers(const IAudioClient::Buffers &buffers, HostProcessData &processData)
{
assignBusBuffers(buffers, processData, true);
}
//------------------------------------------------------------------------
// Vst3Processor
//------------------------------------------------------------------------
Vst3EffectImpl::Vst3EffectImpl()
{
buffers.inputs = nullptr;
buffers.outputs = nullptr;
componentHandler.setEffect(this);
}
//------------------------------------------------------------------------
Vst3EffectImpl::~Vst3EffectImpl()
{
delete[] buffers.inputs;
delete[] buffers.outputs;
terminate();
}
//------------------------------------------------------------------------
void Vst3EffectImpl::SetControl(int index, float value)
{
this->parameterValues[index] = value;
ParamID paramId = lv2ToVstParam[index];
double normalizedValue = controller->plainParamToNormalized(paramId, value);
this->controller->setParamNormalized(paramId, normalizedValue);
{
RtInversionGuard inversionGuard; // boost priority to prevent priority inversion.
std::lock_guard guard{parameterMutex};
paramTransferrer.addChange(paramId, normalizedValue, 0);
}
if (!isProcessing)
{
Run(0, nullptr); // pump parameter changes on UI thread.
}
}
void Vst3EffectImpl::Load(uint64_t instanceId, const Vst3PluginInfo &info, IHost *pHost)
{
processContext = {};
processContext.tempo = 120;
this->pHost = pHost;
this->instanceId = instanceId;
this->info = info;
size_t nControls = info.pluginInfo_.controls().size();
inputParameterChanges.setMaxParameters(nControls);
outputParameterChanges.setMaxParameters(nControls);
lv2ToVstParam.resize(nControls);
parameterValues.resize(nControls);
for (size_t i = 0; i < nControls; ++i)
{
const auto &control = info.pluginInfo_.controls()[i];
ParamID paramId;
std::stringstream ss(control.symbol());
ss >> paramId;
assert(i == control.index());
lv2ToVstParam[control.index()] = paramId;
parameterValues[control.index()] = control.default_value();
if (control.is_bypass())
{
bypassControl = i;
}
}
std::string error;
this->module = Module::create(info.filePath_, error);
if (!module)
{
throw Vst3Exception(SS("Vst3 load failed: " << error << "(" << info.filePath_ << ")"));
}
const PluginFactory &factory = module->getFactory();
ClassInfo myClassInfo;
bool foundClassInfo = false;
VST3::Optional<VST3::UID> myUid = VST3::UID::fromString(info.uid_);
if (!myUid)
{
throw Vst3Exception(SS("Invalid UID"));
}
for (const ClassInfo &classInfo : factory.classInfos())
{
if (classInfo.category() == kVstAudioEffectClass &&
classInfo.ID() == *myUid)
{
myClassInfo = classInfo;
foundClassInfo = true;
}
}
if (!foundClassInfo)
{
throw Vst3Exception(SS("Vst3 load failed: effect class not found in " << info.filePath_));
}
plugProvider = owned(NEW PlugProvider(factory, myClassInfo, true));
this->component = plugProvider->getComponent();
this->processor = component;
this->controller = plugProvider->getController();
controller->queryInterface(IMidiMapping::iid, (void **)&midiMapping);
paramTransferrer.setMaxParameters(1000);
if (midiMapping)
midiCCMapping = initMidiCtrlerAssignment(component, midiMapping);
setBlockSize((Steinberg::int32)pHost->GetMaxAudioBufferSize());
setSamplerate((SampleRate)pHost->GetSampleRate());
ProcessSetup setup{kRealtime, kSample32, blockSize, sampleRate};
initProcessData();
if (processor->setupProcessing(setup) != kResultOk)
{
throw Vst3Exception(SS(info.pluginInfo_.name() << ": setupProcessing() failed."));
}
if (component->setActive(true) != kResultOk)
throw Vst3Exception(SS(info.pluginInfo_.name() << ": setActive() failed."));
OPtr<IBStream> state = new MemoryStream();
assert(GetRefCount(state.get()) == 1);
this->supportsState = false;
if (component->getState(state) == kResultOk)
{
this->supportsState = true;
state->seek(0, IBStream::kIBSeekSet);
controller->setComponentState(state);
refreshControlValues();
}
IComponentHandler *handler;
controller->setComponentHandler(&componentHandler);
paramTransferrer.setMaxParameters(1000);
if (midiMapping)
midiCCMapping = initMidiCtrlerAssignment(component, midiMapping);
component->setActive(true);
for (size_t i = 0; i < this->lv2ToVstParam.size(); ++i)
{
fireControlChanged(i, (float)(controller->getParamNormalized(lv2ToVstParam[i])));
}
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
// void Vst3PluginImpl::createLocalMediaServer (const Name& name)
// {
// mediaServer = createMediaServer (name);
// mediaServer->registerAudioClient (this);
// mediaServer->registerMidiClient (this);
// }
//------------------------------------------------------------------------
//------------------------------------------------------------------------
void Vst3EffectImpl::terminate()
{
// mediaServer = nullptr;
if (!processor)
return;
processor->setProcessing(false);
component->setActive(false);
}
//------------------------------------------------------------------------
void Vst3EffectImpl::initProcessData()
{
// processData.prepare will be done in setBlockSize
buffers.numInputs = info.pluginInfo_.audio_inputs();
buffers.numOutputs = info.pluginInfo_.audio_outputs();
buffers.numSamples = 0;
buffers.inputs = new float *[buffers.numInputs + 1];
buffers.outputs = new float *[buffers.numOutputs + 1];
for (size_t i = 0; i < buffers.numInputs + 1; ++i)
{
buffers.inputs[i] = nullptr;
}
for (size_t i = 0; i < buffers.numOutputs + 1; ++i)
{
buffers.outputs[i] = nullptr;
}
processData.inputEvents = &eventList;
processData.inputParameterChanges = &inputParameterChanges;
processData.outputParameterChanges = &outputParameterChanges;
processData.processContext = &processContext;
setSamplerate(pHost->GetSampleRate());
setBlockSize((Steinberg::int32)pHost->GetMaxAudioBufferSize());
}
//------------------------------------------------------------------------
IMidiClient::IOSetup Vst3EffectImpl::getMidiIOSetup() const
{
IMidiClient::IOSetup iosetup;
auto count = component->getBusCount(MediaTypes::kEvent, BusDirections::kInput);
for (int32_t i = 0; i < count; i++)
{
BusInfo info;
if (component->getBusInfo(MediaTypes::kEvent, BusDirections::kInput, i, info) != kResultOk)
continue;
auto busName = VST3::StringConvert::convert(info.name, 128);
iosetup.inputs.push_back(busName);
}
count = component->getBusCount(MediaTypes::kEvent, BusDirections::kOutput);
for (int32_t i = 0; i < count; i++)
{
BusInfo info;
if (component->getBusInfo(MediaTypes::kEvent, BusDirections::kOutput, i, info) !=
kResultOk)
continue;
auto busName = VST3::StringConvert::convert(info.name, 128);
iosetup.outputs.push_back(busName);
}
return iosetup;
}
//------------------------------------------------------------------------
IAudioClient::IOSetup Vst3EffectImpl::getIOSetup() const
{
IAudioClient::IOSetup iosetup;
auto count = component->getBusCount(MediaTypes::kAudio, BusDirections::kOutput);
for (int32_t i = 0; i < count; i++)
{
BusInfo info;
if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kOutput, i, info) !=
kResultOk)
continue;
for (int32_t j = 0; j < info.channelCount; j++)
{
auto channelName = VST3::StringConvert::convert(info.name, 128);
iosetup.outputs.push_back(channelName + " " + std::to_string(j));
}
}
count = component->getBusCount(MediaTypes::kAudio, BusDirections::kInput);
for (int32_t i = 0; i < count; i++)
{
BusInfo info;
if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kInput, i, info) != kResultOk)
continue;
for (int32_t j = 0; j < info.channelCount; j++)
{
auto channelName = VST3::StringConvert::convert(info.name, 128);
iosetup.inputs.push_back(channelName + " " + std::to_string(j));
}
}
return iosetup;
}
//------------------------------------------------------------------------
void Vst3EffectImpl::preprocess(Buffers &buffers, int64_t continousFrames)
{
processData.numSamples = buffers.numSamples;
processContext.continousTimeSamples = continousFrames;
assignBusBuffers(buffers, processData);
{
std::lock_guard guard{parameterMutex};
paramTransferrer.transferChangesTo(inputParameterChanges);
}
outputParameterChanges.clearQueue();
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::process(Buffers &buffers, int64_t continousFrames)
{
if (!processor || !isProcessing)
return false;
buffers.numSamples = continousFrames;
preprocess(buffers, continousFrames);
if (processor->process(processData) != kResultOk)
return false;
postprocess(buffers);
return true;
}
//------------------------------------------------------------------------
void Vst3EffectImpl::postprocess(Buffers &buffers)
{
eventList.clear();
inputParameterChanges.clearQueue();
unassignBusBuffers(buffers, processData);
}
void Vst3EffectImpl::SendControlChanges(RealtimeRingBufferWriter *realtimeRingBufferWriter)
{
for (auto i = 0; i < outputParameterChanges.getParameterCount(); ++i)
{
IParamValueQueue *queue = outputParameterChanges.getParameterData(i);
auto points = queue->getPointCount();
if (points != 0)
{
Steinberg::Vst::ParamValue value;
Steinberg::int32 sampleOffset;
queue->getPoint(points - 1, sampleOffset, value);
auto paramId = queue->getParameterId();
int thisControlId = -1;
for (size_t lv2Id = 0; lv2Id < this->lv2ToVstParam.size(); ++lv2Id)
{
if (lv2ToVstParam[lv2Id] == paramId)
{
thisControlId = (int)lv2Id;
break;
}
}
if (thisControlId != -1)
{
// realtimeRingBufferWriter->NotifyVst3ControlValue(this->instanceId,thisControlId,(float)value);
}
}
}
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::setSamplerate(SampleRate value)
{
if (sampleRate == value)
return true;
sampleRate = value;
processContext.sampleRate = sampleRate;
if (blockSize == 0)
return true;
return true;
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::setBlockSize(int32 value)
{
blockSize = value;
if (sampleRate == 0)
return true;
processData.prepare(*component, blockSize, kSample32);
return true;
}
//------------------------------------------------------------------------
//------------------------------------------------------------------------
bool Vst3EffectImpl::isPortInRange(int32 port, int32 channel) const
{
return port < kMaxMidiMappingBusses && !midiCCMapping[port][channel].empty();
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::processVstEvent(const IMidiClient::Event &event, int32 port)
{
auto vstEvent = midiToEvent(event.type, event.channel, event.data0, event.data1);
if (vstEvent)
{
vstEvent->busIndex = port;
if (eventList.addEvent(*vstEvent) != kResultOk)
{
assert(false && "Event was not added to EventList!");
}
return true;
}
return false;
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::processParamChange(const IMidiClient::Event &event, int32 port)
{
auto paramMapping = [port, this](int32 channel, MidiData data1) -> ParamID
{
if (!isPortInRange(port, channel))
return kNoParamId;
return midiCCMapping[port][channel][data1];
};
auto paramChange =
midiToParameter(event.type, event.channel, event.data0, event.data1, paramMapping);
if (paramChange)
{
int32 index = 0;
IParamValueQueue *queue =
inputParameterChanges.addParameterData((*paramChange).first, index);
if (queue)
{
if (queue->addPoint(event.timestamp, (*paramChange).second, index) != kResultOk)
{
assert(false && "Parameter point was not added to ParamValueQueue!");
}
}
return true;
}
return false;
}
//------------------------------------------------------------------------
bool Vst3EffectImpl::onEvent(const IMidiClient::Event &event, int32_t port)
{
// Try to create Event first.
if (processVstEvent(event, port))
return true;
// In case this is no event it must be a parameter.
if (processParamChange(event, port))
return true;
// TODO: Something else???
return true;
}
//------------------------------------------------------------------------
void Vst3EffectImpl::Activate()
{
processor->setProcessing(true); // != kResultOk
this->isProcessing = true;
}
void Vst3EffectImpl::Deactivate()
{
if (isProcessing)
{
Run(0, nullptr); // make sure all pending events have been processed.
isProcessing = false;
}
}
void Vst3EffectImpl::Prepare(int32_t sampleRate, size_t maxBufferSize, int inputChannels, int outputChannels)
{
}
void Vst3EffectImpl::Unprepare()
{
if (isProcessing)
{
Deactivate();
}
}
ssize_t Vst3EffectImpl::ParamIdToLv2Id(ParamID id) const
{
for (size_t i = 0; i < this->lv2ToVstParam.size(); ++i)
{
if (lv2ToVstParam[i] == id)
{
return (ssize_t)i;
}
}
return -1;
}
tresult Vst3EffectImpl::beginEdit(ParamID id)
{
return kResultOk;
}
tresult Vst3EffectImpl::performEdit(ParamID id, ParamValue valueNormalized)
{
int ix = ParamIdToLv2Id(id);
if (ix != -1)
{
fireControlChanged(ix, (float)valueNormalized);
}
return kResultOk;
}
tresult Vst3EffectImpl::endEdit(ParamID id)
{
return kResultOk;
}
void Vst3EffectImpl::transferControllerStateToComponent()
{
OPtr<IRtStream> bStream = this->streamPool.AllocateBStream();
assert(GetRefCount(bStream.get()) == 1);
RtInversionGuard inversionGuard; // boost priority to prevent priority inversion.
std::lock_guard guard{parameterMutex};
paramTransferrer.removeChanges();
// assume that parameters are straightforward and uncomplicated if they
// didn't provide BStream-based state management
// Vst2 used to do this. It's not clear that this is still legal in Vst3.
for (int index = 0; index < this->lv2ToVstParam.size(); ++index)
{
ParamID paramId = lv2ToVstParam[index];
paramTransferrer.addChange(paramId,
controller->plainParamToNormalized(paramId, parameterValues[index]),
0);
}
if (!this->isProcessing)
{
Run(0,nullptr);
}
}
tresult Vst3EffectImpl::restartComponent(int32 flags)
{
if (flags & (RestartFlags::kParamValuesChanged))
{
refreshControlValues();
transferControllerStateToComponent();
}
return kResultOk;
}
static std::vector<uint8_t> StreamToVec(IBStream *stream)
{
Steinberg::int64 length;
stream->seek(0, IBStream::kIBSeekEnd, &length);
stream->seek(0, IBStream::kIBSeekSet);
std::vector<uint8_t> result;
result.resize(length);
Steinberg::int32 numRead = 0;
stream->read(&result[0], length, &numRead);
if (numRead != length)
{
throw Vst3Exception("Failed to read.");
}
return result;
}
void Vst3EffectImpl::CheckSync()
{
OPtr<IBStream> stream{new MemoryStream()};
if (this->controller->getState(stream) == kResultOk)
{
std::vector<uint8_t> initialState = StreamToVec(stream);
OPtr<IBStream> stream2{new MemoryStream()};
this->component->getState(stream2);
stream2->seek(0, IBStream::kIBSeekSet);
this->controller->setState(stream2);
OPtr<IBStream> stream3{new MemoryStream()};
this->controller->getState(stream3);
std::vector<uint8_t> newState = StreamToVec(stream3);
if (initialState.size() != newState.size())
{
throw Vst3Exception("State changed.");
}
for (size_t i = 0; i < newState.size(); ++i)
{
if (initialState[i] != newState[i])
{
throw Vst3Exception("State changed.");
}
}
}
}
bool Vst3EffectImpl::GetState(std::vector<uint8_t>*state)
{
OPtr<IBStream> stream{new MemoryStream()};
if (component->getState(stream) != kResultOk)
{
return false;
}
Steinberg::int64 length = 0;
if (stream->seek(0, IBStream::kIBSeekEnd, &length) != kResultOk)
{
return false;
}
stream->seek(0, IBStream::kIBSeekSet);
std::vector<uint8_t> result;
result.resize(length);
int64 ix = 0;
while (length != 0)
{
int32 thisTime;
if (length > 0x10000000)
{
thisTime = 0x10000000;
}
else
{
thisTime = (int32)length;
}
if (stream->read((void *)(&result[ix]), thisTime, &thisTime) != kResultOk)
{
throw Vst3Exception(this->info.pluginInfo_.name() + ": Failed to read state.");
}
if (thisTime == 0)
{
throw Vst3Exception(this->info.pluginInfo_.name() + ": Unexpected end of file while reading state.");
}
ix += thisTime;
length -= thisTime;
}
*state = std::move(result);
return true;
}
void Vst3EffectImpl::SetState(const std::vector<uint8_t> state)
{
OPtr<IBStream> stream{new MemoryStream((void *)(&state[0]), state.size())};
if (controller->setComponentState(stream) != kResultOk)
{
throw Vst3Exception(this->info.pluginInfo_.name() + " Failed to restore state.");
}
refreshControlValues();
stream->seek(0,IBStream::kIBSeekSet);
if (component->setState(stream) != kResultOk)
{
transferControllerStateToComponent();
}
}
void Vst3EffectImpl::refreshControlValues()
{
for (size_t i = 0; i < lv2ToVstParam.size(); ++i)
{
fireControlChanged(i, controller->getParamNormalized(lv2ToVstParam[i]));
}
}
std::vector<Vst3ProgramList> Vst3EffectImpl::GetProgramList(int32_t programListId)
{
std::vector<Vst3ProgramList> result;
FUnknownPtr<IUnitInfo> iUnitInfo(controller);
if (!iUnitInfo)
throw Vst3Exception("Invalid unit info");
int count = iUnitInfo->getProgramListCount();
for (int i = 0; i < count; ++i)
{
ProgramListInfo programListInfo;
if (iUnitInfo->getProgramListInfo(i, programListInfo) != kResultOk)
{
break;
}
if (programListInfo.id == programListId)
{
Vst3ProgramList list;
list.name = VST3::StringConvert::convert(programListInfo.name);
for (int prog = 0; prog < programListInfo.programCount; ++prog)
{
Vst::String128 progName;
if (iUnitInfo->getProgramName(programListInfo.id, prog, progName) == kResultOk)
{
Vst3ProgramListEntry entry;
entry.id = prog;
entry.name = VST3::StringConvert::convert(progName);
list.programs.push_back(std::move(entry));
}
}
result.push_back(std::move(list));
}
}
return result;
}
void Vst3EffectImpl::fireControlChanged(int control, float normalizedValue)
{
normalizedValue = NaNGuard(normalizedValue);
float plainValue = NaNGuard(this->controller->normalizedParamToPlain(this->lv2ToVstParam[control], normalizedValue));
if (parameterValues[control] != plainValue)
{
parameterValues[control] = plainValue;
controlChangedHandler(control,plainValue);
}
}
JSON_MAP_BEGIN(Vst3PluginInfo)
JSON_MAP_REFERENCE(Vst3PluginInfo, filePath)
JSON_MAP_REFERENCE(Vst3PluginInfo, version)
JSON_MAP_REFERENCE(Vst3PluginInfo, uid)
JSON_MAP_REFERENCE(Vst3PluginInfo, pluginInfo)
JSON_MAP_END()
+689
View File
@@ -0,0 +1,689 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "ss.hpp"
#include "PiPedalHost.hpp"
#include "vst3/Vst3Host.hpp"
#include "Lv2Log.hpp"
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <filesystem>
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/iparameterclient.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h"
#include <array>
#include <sstream>
#include "LiteralVersion.hpp"
#include "json.hpp"
using namespace std;
using namespace pipedal;
using namespace VST3::Hosting;
using namespace Steinberg;
using namespace Steinberg::Vst;
namespace pipedal
{
class Vst3HostApplication : public HostApplication
{
public:
Vst3HostApplication() {}
//--- IHostApplication ---------------
tresult PLUGIN_API getName(String128 name)
{
return VST3::StringConvert::convert("PiPedal", name) ? kResultTrue : kInternalError;
}
};
class Vst3HostImpl : public Vst3Host
{
private:
OPtr<Vst3HostApplication> hostApplication;
std::string cacheFilePath;
public:
Vst3HostImpl(const std::string&cacheFilePath);
const Vst3Host::PluginList &RescanPlugins() override;
const Vst3Host::PluginList &RefreshPlugins() override;
const Vst3Host::PluginList &getPluginList() override { return pluginList; }
std::unique_ptr<Vst3Effect> CreatePlugin(long instanceId, const std::string &url, IHost *pHost) override;
std::unique_ptr<Vst3Effect> CreatePlugin(PedalBoardItem&pedalBoardItem, IHost*pHost) override;
private:
Lv2PluginUiInfo *GetPluginInfo(const std::string&uri);
void LoadPluginCache();
void SavePluginCache();
bool havePluginsChanged() const;
PluginList pluginList;
void EnsureContext();
std::unique_ptr<Vst3PluginInfo> MakeDetailedPluginInfo(const PluginFactory &factory, const ClassInfo &classInfo, const std::string &path);
void MakeVst3Info(const std::filesystem::path &path, Vst3Host::PluginList &list);
};
Vst3Host::Ptr Vst3Host::CreateInstance(const std::string&cacheFilePath)
{
return std::make_unique<Vst3HostImpl>(cacheFilePath);
}
//--------------------------------------------------------------
} // namespace pipedal
Vst3HostImpl::Vst3HostImpl(const std::string&cacheFilePath)
:cacheFilePath(cacheFilePath)
{
this->hostApplication = new Vst3HostApplication();
EnsureContext();
}
static bool hasSubCategory(const ClassInfo &classInfo, const std::string &subcategory)
{
for (auto &category : classInfo.subCategories())
{
if (category == subcategory)
{
return true;
}
}
return false;
}
static PluginType getVst3PluginType(const ClassInfo &classInfo)
{
if (hasSubCategory(classInfo, "Instrument"))
{
return PluginType::InstrumentPlugin;
}
if (!hasSubCategory(classInfo, "Fx"))
{
return PluginType::InvalidPlugin;
}
// kFxAnalyzer = "Fx|Analyzer"; ///< Scope, FFT-Display, Loudness Processing...
if (hasSubCategory(classInfo, "Analyzer"))
{
return PluginType::AnalyserPlugin;
}
// kFxDelay = "Fx|Delay"; ///< Delay, Multi-tap Delay, Ping-Pong Delay...
if (hasSubCategory(classInfo, "Delay"))
{
return PluginType::DelayPlugin;
}
// kFxDistortion = "Fx|Distortion"; ///< Amp Simulator, Sub-Harmonic, SoftClipper...
if (hasSubCategory(classInfo, "Distortion"))
{
return PluginType::DistortionPlugin;
}
// kFxDynamics = "Fx|Dynamics"; ///< Compressor, Expander, Gate, Limiter, Maximizer, Tape Simulator, EnvelopeShaper...
if (hasSubCategory(classInfo, "Dynamics"))
{
return PluginType::DynamicsPlugin;
}
// kFxEQ = "Fx|EQ"; ///< Equalization, Graphical EQ...
if (hasSubCategory(classInfo, "EQ"))
{
return PluginType::EQPlugin;
}
// kFxFilter = "Fx|Filter"; ///< WahWah, ToneBooster, Specific Filter,...
if (hasSubCategory(classInfo, "Filter"))
{
return PluginType::FilterPlugin;
}
// kFx = "Fx"; ///< others type (not categorized)
// kFxSpatial = "Fx|Spatial"; ///< MonoToStereo, StereoEnhancer,...
if (hasSubCategory(classInfo, "Spatial"))
{
return PluginType::SpatialPlugin;
}
// kFxGenerator = "Fx|Generator"; ///< Tone Generator, Noise Generator...
if (hasSubCategory(classInfo, "Generator"))
{
return PluginType::GeneratorPlugin;
}
// kFxReverb = "Fx|Reverb"; ///< Reverberation, Room Simulation, Convolution Reverb...
if (hasSubCategory(classInfo, "Reverb"))
{
return PluginType::ReverbPlugin;
}
// kFxPitchShift = "Fx|Pitch Shift"; ///< Pitch Processing, Pitch Correction, Vocal Tuning...
if (hasSubCategory(classInfo, "Pitch Shift"))
{
return PluginType::PitchPlugin;
}
// kFxModulation = "Fx|Modulation"; ///< Phaser, Flanger, Chorus, Tremolo, Vibrato, AutoPan, Rotary, Cloner...
if (hasSubCategory(classInfo, "Modulation"))
{
return PluginType::ModulatorPlugin;
}
// kFxRestoration = "Fx|Restoration"; ///< Denoiser, Declicker,...
if (hasSubCategory(classInfo, "Restoration"))
{
return PluginType::UtilityPlugin;
}
// kFxSurround = "Fx|Surround"; ///< dedicated to surround processing: LFE Splitter, Bass Manager...
if (hasSubCategory(classInfo, "Surround"))
{
return PluginType::UtilityPlugin;
}
// kFxTools = "Fx|Tools"; ///< Volume, Mixer, Tuner...
if (hasSubCategory(classInfo, "Tools"))
{
return PluginType::UtilityPlugin;
}
// kFxMastering = "Fx|Mastering"; ///< Dither, Noise Shaping,...
if (hasSubCategory(classInfo, "Mastering"))
{
return PluginType::UtilityPlugin;
}
// kFxNetwork = "Fx|Network"; ///< using Network
if (hasSubCategory(classInfo, "Network"))
{
return PluginType::UtilityPlugin;
}
// kFxInstrument = "Fx|Instrument"; ///< Fx which could be loaded as Instrument too
// kFxInstrumentExternal = "Fx|Instrument|External"; ///< Fx which could be loaded as Instrument too and is external (wrapped Hardware)
if (hasSubCategory(classInfo, "Instrument"))
{
return PluginType::InstrumentPlugin;
}
return PluginType::Plugin;
}
void Vst3HostImpl::EnsureContext()
{
PluginContextFactory::instance().setPluginContext(this->hostApplication);
}
unique_ptr<Vst3PluginInfo> Vst3HostImpl::MakeDetailedPluginInfo(const PluginFactory &factory, const ClassInfo &classInfo, const std::string &path)
{
IPtr<PlugProvider> plugProvider = owned(NEW PlugProvider(factory, classInfo, true));
{
OPtr<IComponent> component = plugProvider->getComponent();
OPtr<IEditController> controller = plugProvider->getController();
FUnknownPtr<IMidiMapping> midiMapping(controller);
FUnknownPtr<IUnitInfo> iUnitInfo(controller);
unique_ptr<Vst3PluginInfo> info = make_unique<Vst3PluginInfo>();
info->filePath_ = path;
info->version_ = classInfo.version();
info->uid_ = classInfo.ID().toString();
Lv2PluginUiInfo &pluginInfo = info->pluginInfo_;
pluginInfo.uri(SS("vst3:" << classInfo.ID().toString()));
pluginInfo.is_vst3(true);
pluginInfo.name(classInfo.name());
pluginInfo.author_name(classInfo.vendor());
pluginInfo.plugin_type(
getVst3PluginType(classInfo));
pluginInfo.plugin_display_type(""); // fill this in later. VSTHost has list of name translations; we don;'t.
if (iUnitInfo)
{
auto nUnits = iUnitInfo->getUnitCount();
for (int i = 0; i < nUnits; ++i)
{
Steinberg::Vst::UnitInfo unitInfo;
iUnitInfo->getUnitInfo(i, unitInfo);
Lv2PluginUiPortGroup portGroup(
SS(unitInfo.id), VST3::StringConvert::convert(unitInfo.name), SS(unitInfo.parentUnitId), unitInfo.programListId);
pluginInfo.port_groups().push_back(portGroup);
}
}
auto nParams = controller->getParameterCount();
for (int32 param = 0; param < nParams; ++param)
{
Steinberg::Vst::ParameterInfo info;
tresult tErr = controller->getParameterInfo(param, info);
Lv2PluginUiControlPort port;
port.index(param); // used for LV2 purposes.
port.symbol(SS(info.id)); // Used for VST3 purposes. convert to int to specifiy a vst control id.
std::string shortTitle = VST3::StringConvert::convert(info.shortTitle);
std::string title = VST3::StringConvert::convert(info.title);
if (shortTitle.length() == 0)
{
shortTitle = title;
}
port.name(shortTitle);
port.comment(title);
// guard against NaN value (mda Talkbox)
port.display_priority(param);
bool isList = (info.flags & ParameterInfo::kIsList) != ParameterInfo::kNoFlags;
bool canAutomate = (info.flags & ParameterInfo::kCanAutomate) != ParameterInfo::kNoFlags;
bool isProgramChange = (info.flags & ParameterInfo::kIsProgramChange) != ParameterInfo::kNoFlags;
bool isReadOnly = (info.flags & ParameterInfo::kIsReadOnly) != ParameterInfo::kNoFlags;
bool notOnGui = (info.flags & ParameterInfo::kIsHidden) != ParameterInfo::kNoFlags;
bool isBypass = (info.flags & ParameterInfo::kIsBypass) != ParameterInfo::kNoFlags;
port.is_bypass(isBypass);
port.not_on_gui(isReadOnly | notOnGui);
port.min_value(controller->normalizedParamToPlain(info.id,0));
port.max_value(controller->normalizedParamToPlain(info.id,1));
double t = controller->normalizedParamToPlain(info.id,info.defaultNormalizedValue);
if (std::isnan(t) || std::isinf(t))
{
t = 0;
}
port.default_value(t);
port.range_steps(info.stepCount == 0 ? 0 : info.stepCount + 1);
if (isList && !isProgramChange)
{
port.enumeration_property(true);
for (int i = 0; i <= info.stepCount; ++i)
{
double normalizedValue;
if (info.stepCount == 0)
{
normalizedValue = 0;
}
else
{
normalizedValue = i * 1.0 / (info.stepCount);
}
String128 strValue;
if (controller->getParamStringByValue(info.id, normalizedValue, strValue) == kResultOk)
{
Lv2ScalePoint scalePoint((float)controller->normalizedParamToPlain(info.id,normalizedValue), VST3::StringConvert::convert(strValue));
port.scale_points().push_back(scalePoint);
}
}
}
if (info.unitId != 0)
{
port.port_group(SS(info.unitId));
}
std::string units = VST3::StringConvert::convert(info.units);
if (units.size() != 0)
{
if (units == "dB")
{
port.units(Units::db);
} else if (units == "%")
{
port.units(Units::pc);
} else if (units == "Hz")
{
port.units(Units::hz);
} else if (units == "kHz")
{
port.units(Units::khz);
} else if (units == "sec" || units == "s")
{
port.units(Units::s);
} else if (units == "ms")
{
port.units(Units::ms);
} else if (units == "cent")
{
port.units(Units::cent);
} else {
port.units(Units::custom);
port.custom_units(units);
}
}
pluginInfo.controls().push_back(port);
}
auto count = component->getBusCount(MediaTypes::kAudio, BusDirections::kOutput);
for (int32_t i = 0; i < count; i++)
{
BusInfo busInfo;
if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kOutput, i, busInfo) ==
kResultOk)
{
auto channelName = VST3::StringConvert::convert(busInfo.name, 128);
if (busInfo.busType == BusTypes::kMain) // aux channels not currently supported.
{
pluginInfo.audio_outputs(pluginInfo.audio_outputs() + busInfo.channelCount);
}
}
}
count = component->getBusCount(MediaTypes::kAudio, BusDirections::kInput);
for (int32_t i = 0; i < count; i++)
{
BusInfo busInfo;
if (component->getBusInfo(MediaTypes::kAudio, BusDirections::kInput, i, busInfo) ==
kResultOk)
{
auto channelName = VST3::StringConvert::convert(busInfo.name, 128);
if (busInfo.busType == BusTypes::kMain) // aux channels not currently supported.
{
pluginInfo.audio_inputs(pluginInfo.audio_inputs() + busInfo.channelCount);
}
}
}
count = component->getBusCount(MediaTypes::kEvent, BusDirections::kInput);
if (count >= 1)
{
pluginInfo.has_midi_input(1);
}
count = component->getBusCount(MediaTypes::kEvent, BusDirections::kOutput);
if (count >= 1)
{
pluginInfo.has_midi_output(1);
}
return info;
}
}
void Vst3HostImpl::MakeVst3Info(const std::filesystem::path &path, Vst3Host::PluginList &list)
{
EnsureContext();
unique_ptr<Vst3PluginInfo> result = std::make_unique<Vst3PluginInfo>();
result->filePath_ = path;
string error;
Module::Ptr module = Module::create(path.string(), error);
if (!module)
{
Lv2Log::error(SS("Vst3 load failed: " << error << "(" << path.string() << ")"));
return;
}
const PluginFactory &factory = module->getFactory();
for (const ClassInfo &classInfo : factory.classInfos())
{
if (classInfo.category() == kVstAudioEffectClass)
{
bool found = false;
std::string newUid = classInfo.ID().toString();
for (const auto &existingPlugin : list)
{
if (existingPlugin->uid_ == newUid)
{
found = true;
break; // first instance takes precedence (e.g. ~/.vst3 takes precedence over /usr/lib/vst3 )
}
}
if (!found)
{
std::unique_ptr<Vst3PluginInfo> pluginInfo = MakeDetailedPluginInfo(factory, classInfo, path);
list.push_back(std::move(pluginInfo));
}
}
}
}
bool Vst3HostImpl::havePluginsChanged() const
{
std::unordered_set<std::string> modules;
for (const auto &plugin : pluginList)
{
if (!std::filesystem::exists(plugin->filePath_))
{
return true;
}
modules.insert(plugin->filePath_);
}
Module::PathList pathList = Module::getModulePaths();
for (const auto &path : pathList)
{
if (!modules.contains(path))
{
return true;
}
}
return false;
}
const Vst3Host::PluginList &Vst3HostImpl::RefreshPlugins()
{
if (cacheFilePath.length() != 0)
{
LoadPluginCache();
}
if (havePluginsChanged())
{
RescanPlugins();
}
return pluginList;
}
const Vst3Host::PluginList &Vst3HostImpl::RescanPlugins()
{
Module::PathList pathList = Module::getModulePaths();
PluginList list;
//pathList.insert(pathList.begin(),"/home/pi/.vst3/mda-vst3.vst3");
for (auto &path : pathList)
{
if (path == std::string("/home/pi/.vst3/mda-vst3.vst3"))
{
Lv2Log::debug("Processing MDA plugins");
}
filesystem::path p{path};
MakeVst3Info(p, list);
}
this->pluginList = std::move(list);
SavePluginCache();
return pluginList;
}
static std::string ParseVst3Url(const std::string &url)
{
if (!url.starts_with("vst3:"))
{
throw Vst3Exception("Not a valid vst3 url.: " + url);
}
std::string strUrl = url.substr(5);
return strUrl;
}
std::unique_ptr<Vst3Effect> Vst3HostImpl::CreatePlugin(long instanceId, const std::string &url, IHost *pHost)
{
EnsureContext();
auto uuid = ParseVst3Url(url);
std::unique_ptr<Vst3Effect> result;
for (const auto &plugin : pluginList)
{
if (plugin->uid_ == uuid)
{
return Vst3Effect::CreateInstance(instanceId, *(plugin.get()), pHost);
}
}
throw Vst3Exception("Plugin not found.");
}
static inline uint8_t Hex(char c)
{
if (c >= '0' && c <= '9') return c-'0';
if (c >= 'a' && c <= 'z') return c-'a'+10;
if (c >= 'A' && c <= 'Z') return c-'A'+10;
throw Vst3Exception("Invalid state bundle.");
}
static std::vector<uint8_t> HexToByteArray(const std::string&hexState)
{
std::vector<uint8_t> result;
result.resize(hexState.length()/2);
for (int i = 0; i < result.size(); ++i)
{
result[i] = Hex(hexState[i*2])*16 + Hex(hexState[i*2+1]);
}
return result;
}
std::unique_ptr<Vst3Effect> Vst3HostImpl::CreatePlugin(PedalBoardItem&pedalBoardItem, IHost*pHost)
{
std::unique_ptr<Vst3Effect> result = CreatePlugin(pedalBoardItem.instanceId(),pedalBoardItem.uri(),pHost);
auto pluginInfo = this->GetPluginInfo(pedalBoardItem.uri());
if (!pluginInfo)
{
throw Vst3Exception(SS("Plugin " << pedalBoardItem.pluginName() << " not found."));
}
if (pedalBoardItem.vstState().length() != 0)
{
std::vector<uint8_t> state = HexToByteArray(pedalBoardItem.vstState());
result->SetState(state);
for (ControlValue &controlValue: pedalBoardItem.controlValues())
{
int32_t index = -1;
for (size_t i = 0; i < pluginInfo->controls().size(); ++i)
{
if (pluginInfo->controls()[i].symbol() == controlValue.key())
{
index = (int32_t)(pluginInfo->controls()[i].index());
break;
}
}
if (index != -1)
{
float value = result->GetControlValue(index);
controlValue.value(value);
}
}
} else {
for (const ControlValue &controlValue: pedalBoardItem.controlValues())
{
int32_t index = -1;
for (size_t i = 0; i < pluginInfo->controls().size(); ++i)
{
if (pluginInfo->controls()[i].symbol() == controlValue.key())
{
index = (int32_t)(pluginInfo->controls()[i].index());
break;
}
}
if (index != -1)
{
result->SetControl(index,controlValue.value());
}
}
}
return result;
}
#include <fstream>
void Vst3HostImpl::LoadPluginCache()
{
if (cacheFilePath.length() != 0)
{
std::ifstream f(cacheFilePath);
if (f.is_open())
{
json_reader reader(f);
reader.read(&(this->pluginList));
}
}
}
void Vst3HostImpl::SavePluginCache()
{
if (cacheFilePath.length() != 0)
{
std::ofstream f(cacheFilePath);
if (f.is_open())
{
json_writer writer(f);
writer.write(this->pluginList);
}
}
}
Lv2PluginUiInfo *Vst3HostImpl::GetPluginInfo(const std::string&uri)
{
for (const auto &pluginInfo: pluginList)
{
if (pluginInfo->pluginInfo_.uri() == uri)
{
return &(pluginInfo->pluginInfo_);
}
}
return nullptr;
}
+159
View File
@@ -0,0 +1,159 @@
//-----------------------------------------------------------------------------
// Flags : clang-format auto
// Project : VST SDK
//
// Category : AudioHost
// Filename : public.sdk/samples/vst-hosting/audiohost/source/media/miditovst.h
// Created by : Steinberg 09.2016
// Description : Audio Host Example for VST 3
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------
#pragma once
#include "public.sdk/source/vst/utility/optional.h"
#include "pluginterfaces/vst/ivstevents.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include <functional>
#include <limits>
//------------------------------------------------------------------------
namespace Steinberg {
namespace Vst {
const uint8_t kNoteOff = 0x80; ///< note, off velocity
const uint8_t kNoteOn = 0x90; ///< note, on velocity
const uint8_t kPolyPressure = 0xA0; ///< note, pressure
const uint8_t kController = 0xB0; ///< controller, value
const uint8_t kProgramChangeStatus = 0xC0; ///< program change
const uint8_t kAfterTouchStatus = 0xD0; ///< channel pressure
const uint8_t kPitchBendStatus = 0xE0; ///< lsb, msb
static const uint32 kDataMask = 0x7F;
const float kMidiScaler = 1.f / 127.f;
using MidiData = uint8_t;
float toNormalized (const MidiData& data)
{
return (float)data * kMidiScaler;
}
using OptionalEvent = VST3::Optional<Event>;
using ParameterChange = std::pair<ParamID, ParamValue>;
using OptionParamChange = VST3::Optional<ParameterChange>;
OptionalEvent midiToEvent (MidiData status, MidiData channel, MidiData midiData0,
MidiData midiData1)
{
Event new_event = {};
if (status == kNoteOn || status == kNoteOff)
{
if (status == kNoteOff) // note off
{
new_event.noteOff.noteId = -1;
new_event.type = Event::kNoteOffEvent;
new_event.noteOff.channel = channel;
new_event.noteOff.pitch = midiData0;
new_event.noteOff.velocity = toNormalized (midiData1);
return std::move (new_event);
}
else if (status == kNoteOn) // note on
{
new_event.noteOn.noteId = -1;
new_event.type = Event::kNoteOnEvent;
new_event.noteOn.channel = channel;
new_event.noteOn.pitch = midiData0;
new_event.noteOn.velocity = toNormalized (midiData1);
return std::move (new_event);
}
}
//--- -----------------------------
else if (status == kPolyPressure)
{
new_event.type = Vst::Event::kPolyPressureEvent;
new_event.polyPressure.channel = channel;
new_event.polyPressure.pitch = midiData0;
new_event.polyPressure.pressure = toNormalized (midiData1);
return std::move (new_event);
}
return {};
}
//------------------------------------------------------------------------
using ToParameterIdFunc = std::function<ParamID (int32, MidiData)>;
OptionParamChange midiToParameter (MidiData status, MidiData channel, MidiData midiData1,
MidiData midiData2, const ToParameterIdFunc& toParamID)
{
if (!toParamID)
return {};
ParameterChange paramChange;
if (status == kController) // controller
{
paramChange.first = toParamID (channel, midiData1);
if (paramChange.first != kNoParamId)
{
paramChange.second = (double)midiData2 * kMidiScaler;
return std::move (paramChange);
}
}
else if (status == kPitchBendStatus)
{
paramChange.first = toParamID (channel, Vst::kPitchBend);
if (paramChange.first != kNoParamId)
{
const double kPitchWheelScaler = 1. / (double)0x3FFF;
const int32 ctrl = (midiData1 & kDataMask) | (midiData2 & kDataMask) << 7;
paramChange.second = kPitchWheelScaler * (double)ctrl;
return std::move (paramChange);
};
}
else if (status == kAfterTouchStatus)
{
paramChange.first = toParamID (channel, Vst::kAfterTouch);
if (paramChange.first != kNoParamId)
{
paramChange.second = (ParamValue) (midiData1 & kDataMask) * kMidiScaler;
return std::move (paramChange);
};
}
else if (status == kProgramChangeStatus)
{
// TODO
}
return {};
}
//------------------------------------------------------------------------
} // Vst
} // Steinberg
+86
View File
@@ -0,0 +1,86 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "vst3/Vst3PresetFile.hpp"
#include "public.sdk/source/vst/vstpresetfile.h"
using namespace pipedal;
using namespace Steinberg;
using namespace Steinberg::Vst;
namespace pipedal {
class Vst3PresetFileImpl : public Vst3PresetFile {
public:
virtual ~Vst3PresetFileImpl() { }
virtual void Load(const std::string&path);
virtual std::vector<Vst3PresetInfo> GetPresets();
virtual std::vector<uint8_t> GetPresetChunk(uint32_t index);
private:
std::unique_ptr<PresetFile> presetFile;
};
void Vst3PresetFileImpl::Load(const std::string&path)
{
OPtr<IBStream> stream = FileStream::open(path.c_str(),"r");
presetFile = std::make_unique<PresetFile>(stream);
}
std::vector<Vst3PresetInfo> Vst3PresetFileImpl::GetPresets()
{
std::vector<Vst3PresetInfo> result;
auto count = presetFile->getEntryCount();
result.reserve(count);
for (int i = 0; i < count; ++i)
{
const PresetFile::Entry& entry = presetFile->at(i);
}
return result;
}
std::vector<uint8_t> Vst3PresetFileImpl::GetPresetChunk(uint32_t index)
{
return std::vector<uint8_t>(); // STUB
}
std::unique_ptr<Vst3PresetFile> Vst3PresetFile::CreateInstance()
{
return std::make_unique<Vst3PresetFileImpl>();
}
} // namespace pipedal
+358
View File
@@ -0,0 +1,358 @@
//-----------------------------------------------------------------------------
// Project : SDK Core
//
// Category : Common Classes
// Filename : public.sdk/source/common/memorystream.cpp
// Created by : Steinberg, 03/2008
// Description : IBStream Implementation for memory blocks
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2022, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Steinberg Media Technologies nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------
#include "vst3/Vst3RtStream.hpp"
#include "pluginterfaces/base/futils.h"
#include <stdlib.h>
namespace pipedal {
//-----------------------------------------------------------------------------
IMPLEMENT_FUNKNOWN_METHODS (RtStream, IBStream, IBStream::iid)
static const TSize kMemGrowAmount = 4096;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
RtStream::RtStream(RtStreamPool*pool)
: pool(pool)
, memory (nullptr)
, memorySize (0)
, size (0)
, cursor (0)
, ownMemory (true)
, allocationError (false)
{
FUNKNOWN_CTOR
}
//-----------------------------------------------------------------------------
RtStream::~RtStream ()
{
if (ownMemory && memory)
::free (memory);
FUNKNOWN_DTOR
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API RtStream::read (void* data, int32 numBytes, int32* numBytesRead)
{
if (memory == nullptr)
{
if (allocationError)
return kOutOfMemory;
numBytes = 0;
}
else
{
// Does read exceed size ?
if (cursor + numBytes > size)
{
int32 maxBytes = int32 (size - cursor);
// Has length become zero or negative ?
if (maxBytes <= 0)
{
cursor = size;
numBytes = 0;
}
else
numBytes = maxBytes;
}
if (numBytes)
{
memcpy (data, &memory[cursor], static_cast<size_t> (numBytes));
cursor += numBytes;
}
}
if (numBytesRead)
*numBytesRead = numBytes;
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API RtStream::write (void* buffer, int32 numBytes, int32* numBytesWritten)
{
if (allocationError)
return kOutOfMemory;
if (buffer == nullptr)
return kInvalidArgument;
// Does write exceed size ?
TSize requiredSize = cursor + numBytes;
if (requiredSize > size)
{
if (requiredSize > memorySize)
setSize (requiredSize);
else
size = requiredSize;
}
// Copy data
if (memory && cursor >= 0 && numBytes > 0)
{
memcpy (&memory[cursor], buffer, static_cast<size_t> (numBytes));
// Update cursor
cursor += numBytes;
}
else
numBytes = 0;
if (numBytesWritten)
*numBytesWritten = numBytes;
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API RtStream::seek (int64 pos, int32 mode, int64* result)
{
switch (mode)
{
case kIBSeekSet:
cursor = pos;
break;
case kIBSeekCur:
cursor = cursor + pos;
break;
case kIBSeekEnd:
cursor = size + pos;
break;
}
if (ownMemory == false)
if (cursor > memorySize)
cursor = memorySize;
if (result)
*result = cursor;
return kResultTrue;
}
//-----------------------------------------------------------------------------
tresult PLUGIN_API RtStream::tell (int64* pos)
{
if (!pos)
return kInvalidArgument;
*pos = cursor;
return kResultTrue;
}
//------------------------------------------------------------------------
TSize RtStream::getSize () const
{
return size;
}
//------------------------------------------------------------------------
void RtStream::setSize (TSize s)
{
if (s <= 0)
{
if (ownMemory && memory)
free (memory);
memory = nullptr;
memorySize = 0;
size = 0;
cursor = 0;
return;
}
TSize newMemorySize = (((Max (memorySize, s) - 1) / kMemGrowAmount) + 1) * kMemGrowAmount;
if (newMemorySize == memorySize)
{
size = s;
return;
}
if (memory && ownMemory == false)
{
allocationError = true;
return;
}
ownMemory = true;
char* newMemory = nullptr;
if (memory)
{
newMemory = (char*)realloc (memory, (size_t)newMemorySize);
if (newMemory == nullptr && newMemorySize > 0)
{
newMemory = (char*)malloc ((size_t)newMemorySize);
if (newMemory)
{
memcpy (newMemory, memory, (size_t)Min (newMemorySize, memorySize));
free (memory);
}
}
}
else
newMemory = (char*)malloc ((size_t)newMemorySize);
if (newMemory == nullptr)
{
if (newMemorySize > 0)
allocationError = true;
memory = nullptr;
memorySize = 0;
size = 0;
cursor = 0;
}
else
{
memory = newMemory;
memorySize = newMemorySize;
size = s;
}
}
//------------------------------------------------------------------------
char* RtStream::getData () const
{
return memory;
}
//------------------------------------------------------------------------
char* RtStream::detachData ()
{
if (ownMemory)
{
char* result = memory;
memory = nullptr;
memorySize = 0;
size = 0;
cursor = 0;
return result;
}
return nullptr;
}
//------------------------------------------------------------------------
bool RtStream::truncate ()
{
if (ownMemory == false)
return false;
if (memorySize == size)
return true;
memorySize = size;
if (memorySize == 0)
{
if (memory)
{
free (memory);
memory = nullptr;
}
}
else
{
if (memory)
{
char* newMemory = (char*)realloc (memory, (size_t)memorySize);
if (newMemory)
memory = newMemory;
}
}
return true;
}
//------------------------------------------------------------------------
bool RtStream::truncateToCursor ()
{
size = cursor;
return truncate ();
}
}
// namespace
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "vst3/Vst3RtStream.hpp"
using namespace Steinberg;
using namespace pipedal;
void RtStreamPool::ReleaseStreams()
{
RtStream *freeStreamList;
{
freeStreamList = this->freeStreamList;
this->freeStreamList = nullptr;
}
while (freeStreamList)
{
RtStream *next = freeStreamList->next;
freeStreamList->release();
freeStreamList = next;
}
}
+49
View File
@@ -0,0 +1,49 @@
#include <string>
#include "public.sdk/source/vst/hosting/module.h"
#include "public.sdk/source/vst/hosting/hostclasses.h"
#include "public.sdk/source/vst/hosting/plugprovider.h"
#include "pluginterfaces/vst/ivsteditcontroller.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstunits.h"
#include "public.sdk/source/vst/utility/stringconvert.h"
#include "public.sdk/source/vst/hosting/eventlist.h"
#include "public.sdk/source/vst/hosting/parameterchanges.h"
#include "public.sdk/source/vst/hosting/processdata.h"
#include "pluginterfaces/vst/ivstaudioprocessor.h"
#include "pluginterfaces/vst/ivstmidicontrollers.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/iparameterclient.h"
#include "public.sdk/samples/vst-hosting/audiohost/source/media/imediaserver.h"
using namespace std;
using namespace VST3::Hosting;
using namespace Steinberg;
using namespace Steinberg::Vst;
PluginFactory::ClassInfos GetVst3ClassInfos(const std::string& pluginPath)
{
std::string error;
Module::Ptr module = Module::create(pluginPath.c_str(), error);
if (!module)
{
assert(false && "Vst3 load failed.");
}
const PluginFactory &factory = module->getFactory();
return factory.classInfos();
}
static std::string HomePath() { return getenv("HOME"); }
void BugReportTest()
{
auto _ = GetVst3ClassInfos(HomePath() + "/.vst3/adelay.vst3");
PluginFactory::ClassInfos classInfos = GetVst3ClassInfos(HomePath() + "/.vst3/mda-vst3.vst3");
// right size.
assert(classInfos.size() == 68); // succeeds
// wrong classInfo (from adelay.vst3)
assert(classInfos[0].name() == "mda Ambience"); // actual result "ADelay"
}
+149
View File
@@ -0,0 +1,149 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "PiPedalHost.hpp"
#include "vst3/Vst3Host.hpp"
#include <iostream>
using namespace pipedal;
using namespace std;
void EnumerateVst3Plugins()
{
cout << "--- Enumeration Test --------" << endl;
Vst3Host::Ptr vst3Host = Vst3Host::CreateInstance();
vst3Host->RescanPlugins();
vst3Host->RefreshPlugins();
}
void CheckSync(IEffect*effect)
{
Vst3Effect *vst3Effect = (Vst3Effect*)effect;
vst3Effect->CheckSync();
}
void TestPrograms(const Vst3PluginInfo &info, Vst3Effect *effect)
{
for (size_t i = 0; i < info.pluginInfo_.port_groups().size(); ++i)
{
const auto &port_group = info.pluginInfo_.port_groups()[i];
if (port_group.program_list_id() != -1)
{
auto programList = effect->GetProgramList(port_group.program_list_id());
for (const auto& programGroup: programList)
{
cout <<" " << port_group.program_list_id() << " " << programGroup.name << " (" << programGroup.programs.size() << " programs)" << std::endl;
// for (const auto&program: programGroup.programs)
// {
// cout << " " << program.id << " " << program.name << endl;
// }
}
}
}
}
void RunVsts()
{
cout << "--- RunVsts ----------------" << endl;
Vst3Host::Ptr vst3Host = Vst3Host::CreateInstance();
cout << "Scanning" << endl;
const auto & plugins = vst3Host->RescanPlugins();
PiPedalHost host;
IHost *pHost = host.asIHost();
host.setSampleRate(44100);
for (const auto & info : plugins)
{
cout << " Running " << info->pluginInfo_.name() << endl;
auto plugin = vst3Host->CreatePlugin(0,info->pluginInfo_.uri(),pHost);
int nInputs = info->pluginInfo_.audio_inputs();
int nOutputs = info->pluginInfo_.audio_outputs();
plugin->Prepare(host.GetSampleRate(),pHost->GetMaxAudioBufferSize(),info->pluginInfo_.audio_inputs(), info->pluginInfo_.audio_outputs());
plugin->Activate();
std::vector<float*> inputs;
std::vector<float*> outputs;
inputs.resize(nInputs);
for (int i = 0; i < nInputs; ++i)
{
inputs[i] = new float[512];
plugin->SetAudioInputBuffer(i,inputs[i]);
for (int j = 0; j < 512; ++j)
{
inputs[i][j] = j / 512.0;
}
}
outputs.resize(nOutputs);
for (int i = 0; i < nOutputs; ++i)
{
outputs[i] = new float[512];
plugin->SetAudioOutputBuffer(i,outputs[i]);
}
plugin->Run(512,nullptr);
for (int i = 0; i < info->pluginInfo_.controls().size(); ++i)
{
float v = plugin->GetControlValue(i);
plugin->SetControl(i,v);
plugin->Run(0,nullptr);
plugin->CheckSync();
}
TestPrograms(*info,plugin.get());
plugin->Deactivate();
}
}
void BugReportTest();
int main(int, char**)
{
BugReportTest();
RunVsts();
EnumerateVst3Plugins();
return 0;
}
+1
View File
@@ -25,6 +25,7 @@ extern "C" {
"detect_leaks=0" // undesirable behavior on abnormal termination. "detect_leaks=0" // undesirable behavior on abnormal termination.
":alloc_dealloc_mismatch=0" // Guitarix components trigger this. It's not actually a problem on GCC. ":alloc_dealloc_mismatch=0" // Guitarix components trigger this. It's not actually a problem on GCC.
":new_delete_type_mismatch=0" //GxTuner ":new_delete_type_mismatch=0" //GxTuner
":detect_odr_violation=0"
":print_stacktrace=1" ":print_stacktrace=1"
":halt_on_error=1" ":halt_on_error=1"
; ;
+1 -1
View File
@@ -27,7 +27,7 @@
#include "AvahiService.hpp" #include "AvahiService.hpp"
#include "PiPedalSocket.hpp" #include "PiPedalSocket.hpp"
#include "Lv2Host.hpp" #include "PiPedalHost.hpp"
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include <filesystem> #include <filesystem>
#include "PiPedalConfiguration.hpp" #include "PiPedalConfiguration.hpp"
+83
View File
@@ -0,0 +1,83 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "PiPedalHost.hpp"
#include <functional>
#include "json.hpp"
namespace pipedal {
struct Vst3PluginInfo {
public:
std::string filePath_;
std::string version_;
std::string uid_;
Lv2PluginUiInfo pluginInfo_;
DECLARE_JSON_MAP(Vst3PluginInfo);
};
struct Vst3ProgramListEntry {
int32_t id;
std::string name;
};
struct Vst3ProgramList {
std::string name;
std::vector<Vst3ProgramListEntry> programs;
};
class Vst3Effect: public IEffect {
public:
using Ptr = std::unique_ptr<Vst3Effect>;
virtual void Prepare(int32_t sampleRate, size_t maxBufferSize,int inputChannels, int outputChannels) = 0;
virtual void Activate() = 0;
virtual void Deactivate() = 0;
virtual void Unprepare() = 0;
virtual void CheckSync() = 0; // throw if audioProcessor and controller are not sync (test only)
virtual bool SupportsState() const = 0;
virtual bool GetState(std::vector<uint8_t> *state) = 0;
virtual void SetState(const std::vector<uint8_t> state) = 0;
using ControlChangedHandler = std::function<void(int control,float value)>;
virtual void SetControlChangedHandler(const ControlChangedHandler& handler) = 0;
virtual void RemoveControlChangedHandler() = 0;
virtual std::vector<Vst3ProgramList> GetProgramList(int32_t programListId) = 0;
public:
static std::unique_ptr<Vst3Effect> CreateInstance(uint64_t instanceId,const Vst3PluginInfo& info, IHost *pHost);
};
}
+286
View File
@@ -0,0 +1,286 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "Vst3Effect.hpp"
#include <vector>
#include "RtInversionGuard.hpp"
#include "Vst3RtStream.hpp"
#pragma once
namespace pipedal
{
using namespace std;
using namespace pipedal;
using namespace VST3::Hosting;
using namespace Steinberg;
using namespace Steinberg::Vst;
enum
{
kMaxMidiMappingBusses = 4,
kMaxMidiChannels = 16
};
using Controllers = std::vector<int32>;
using Channels = std::array<Controllers, kMaxMidiChannels>;
using Busses = std::array<Channels, kMaxMidiMappingBusses>;
using MidiCCMapping = Busses;
class Vst3EffectImpl : public Vst3Effect,
public IAudioClient,
public IMidiClient
{
public:
//--------------------------------------------------------------------
using Name = std::string;
Vst3EffectImpl();
virtual ~Vst3EffectImpl();
void Load(uint64_t instanceId, const Vst3PluginInfo &info, IHost *pHost);
// IEffect
virtual bool IsVst3() const { return true; }
virtual uint64_t GetInstanceId() const { return instanceId; }
virtual int GetControlIndex(const std::string &symbol) const
{
for (size_t i = 0; i < info.pluginInfo_.controls().size(); ++i)
{
if (info.pluginInfo_.controls()[i].symbol() == symbol)
{
return (int)i;
}
}
return -1;
}
bool SupportsState() const { return supportsState; }
bool GetState(std::vector<uint8_t> *state);
void SetState(const std::vector<uint8_t> state);
virtual void CheckSync();
//- PiPedalHost Interfaces.
virtual void SetControl(int index, float value);
virtual float GetControlValue(int index) const
{
return this->parameterValues[index];
}
virtual std::vector<Vst3ProgramList> GetProgramList(int32_t programListId);
int bypassControl = -1;
bool bypassEnable = false;
virtual void SetBypass(bool enable)
{
if (bypassControl != -1)
{
SetControl(bypassControl, enable ? 1 : 0);
}
else
{
bypassEnable = enable;
}
}
virtual void Activate();
virtual void Deactivate();
virtual float GetOutputControlValue(int controlIndex) const
{
return this->parameterValues[controlIndex];
}
virtual int GetNumberOfInputAudioPorts() const
{
return info.pluginInfo_.audio_inputs();
}
virtual int GetNumberOfOutputAudioPorts() const
{
return info.pluginInfo_.audio_outputs();
}
virtual float *GetAudioInputBuffer(int index) const { return buffers.inputs[index]; }
virtual float *GetAudioOutputBuffer(int index) const { return buffers.outputs[index]; }
virtual void ResetAtomBuffers() {}
virtual void RequestParameter(LV2_URID uridUri) {} // no vst equivalent.
virtual void GatherParameter(RealtimeParameterRequest *pRequest) {} // no vst equivalent.
virtual void SetAudioInputBuffer(int index, float *buffer)
{
buffers.inputs[index] = buffer;
}
virtual void SetAudioOutputBuffer(int index, float *buffer)
{
buffers.outputs[index] = buffer;
}
virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter)
{
process(buffers, samples);
SendControlChanges(realtimeRingBufferWriter);
}
// Vst3Effect interfaces.
virtual void Prepare(int32_t sampleRate, size_t maxBufferSize, int inputChannels, int outputChannels);
virtual void Unprepare();
// IAudioClient
bool process(Buffers &buffers, int64_t continousFrames) override;
bool setSamplerate(SampleRate value) override;
bool setBlockSize(int32 value) override;
IAudioClient::IOSetup getIOSetup() const override;
// IMidiClient
bool onEvent(const Event &event, int32_t port) override;
IMidiClient::IOSetup getMidiIOSetup() const override;
// IParameterClient
//--------------------------------------------------------------------
private:
bool supportsState = false;
IHost *pHost;
Module::Ptr module;
IPtr<IMidiMapping> midiMapping;
IPtr<PlugProvider> plugProvider;
RtStreamPool streamPool;
void SendControlChanges(RealtimeRingBufferWriter *realtimeRingBufferWriter);
//--------------------------------------------------------------------
private:
std::mutex parameterMutex;
Buffers buffers;
std::vector<ParamID> lv2ToVstParam;
std::vector<float> parameterValues;
// void createLocalMediaServer(const Name &name);
uint64_t instanceId;
Vst3PluginInfo info;
void terminate();
void initProcessData();
void updateBusBuffers(Buffers &buffers, HostProcessData &processData);
void preprocess(Buffers &buffers, int64_t continousFrames);
void postprocess(Buffers &buffers);
bool isPortInRange(int32 port, int32 channel) const;
bool processVstEvent(const IMidiClient::Event &event, int32 port);
bool processParamChange(const IMidiClient::Event &event, int32 port);
SampleRate sampleRate = 0;
int32 blockSize = 0;
HostProcessData processData;
ProcessContext processContext;
EventList eventList;
ParameterChanges inputParameterChanges;
ParameterChanges outputParameterChanges;
IComponent *component = nullptr;
IEditController *controller = nullptr;
FUnknownPtr<IAudioProcessor> processor;
ParameterChangeTransfer paramTransferrer;
MidiCCMapping midiCCMapping;
// IMediaServerPtr mediaServer;
bool isProcessing = false;
Name name;
private:
ssize_t ParamIdToLv2Id(ParamID id) const;
class ComponentHandler : public IComponentHandler
{
private:
Vst3EffectImpl *pEffect = nullptr;
public:
ComponentHandler() { }
void setEffect(Vst3EffectImpl*effect)
{
pEffect = effect;
}
tresult PLUGIN_API beginEdit(ParamID id) override
{
return pEffect->beginEdit(id);
}
tresult PLUGIN_API performEdit(ParamID id, ParamValue valueNormalized) override
{
return pEffect->performEdit(id, valueNormalized);
}
tresult PLUGIN_API endEdit(ParamID id) override
{
return pEffect->endEdit(id);
}
tresult PLUGIN_API restartComponent(int32 flags) override
{
return pEffect->restartComponent(flags);
}
private:
tresult PLUGIN_API queryInterface(const TUID _iid, void ** obj) override
{
if (_iid == IComponentHandler_iid)
{
addRef();
*((FUnknown**)obj) = this;
return kResultOk;
}
return kNoInterface;
}
uint32 PLUGIN_API addRef() override { return 1000; }
uint32 PLUGIN_API release() override { return 1000; }
};
ComponentHandler componentHandler;
void transferControllerStateToComponent();
void refreshControlValues();
virtual tresult beginEdit(ParamID id);
virtual tresult performEdit(ParamID id, ParamValue valueNormalized);
virtual tresult endEdit(ParamID id);
virtual tresult restartComponent(int32 flags);
private:
ControlChangedHandler controlChangedHandler = [] (int control,float value) {};
void fireControlChanged(int control, float normalizedValue);
public:
virtual void SetControlChangedHandler(const ControlChangedHandler& handler)
{
controlChangedHandler = handler;
}
virtual void RemoveControlChangedHandler() {
controlChangedHandler = [] (int,float){};
}
};
}
+70
View File
@@ -0,0 +1,70 @@
/*
/* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <vector>
#include <stdint.h>
#include <string>
#include <exception>
#include <stdexcept>
#include "public.sdk/source/vst/utility/uid.h"
#include "vst3/Vst3Effect.hpp"
#include "PedalBoard.hpp"
namespace pipedal {
class Vst3Exception: public std::logic_error {
public:
Vst3Exception(const std::string&error)
:logic_error(error)
{
}
};
class Vst3Host {
public:
using Ptr = std::unique_ptr<Vst3Host>;
using PluginList = std::vector<std::unique_ptr<Vst3PluginInfo>>;
virtual ~Vst3Host() {}
virtual const PluginList &RescanPlugins() = 0;
virtual const PluginList &RefreshPlugins() = 0;
virtual const PluginList &getPluginList() = 0;
virtual std::unique_ptr<Vst3Effect> CreatePlugin(long instanceId,const std::string&url, IHost*pHost) = 0;
virtual std::unique_ptr<Vst3Effect> CreatePlugin(PedalBoardItem&pedalBoardItem, IHost*pHost) = 0;
static Ptr CreateInstance(const std::string &cacheFilePath = "");
};
};
+53
View File
@@ -0,0 +1,53 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <cstddef>
#include <string>
#include <vector>
#include <memory>
namespace pipedal {
struct Vst3PresetInfo {
uint32_t index;
std::string name;
};
class Vst3PresetFile {
protected:
Vst3PresetFile() { }
public:
virtual ~Vst3PresetFile() { }
virtual void Load(const std::string&path) = 0;
virtual std::vector<Vst3PresetInfo> GetPresets() = 0;
virtual std::vector<uint8_t> GetPresetChunk(uint32_t index) = 0;
std::unique_ptr<Vst3PresetFile> CreateInstance();
};
}
+109
View File
@@ -0,0 +1,109 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. Davies
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include "public.sdk/source/common/memorystream.h"
#include <mutex>
#include "Vst3RtStream.hpp"
namespace pipedal
{
using namespace Steinberg;
class IRtStream : public IBStream
{
public:
virtual void QueueForRelease() = 0;
};
class RtStreamPool;
class RtStream : public IRtStream
{
friend class RtStreamPool;
virtual void PLUGIN_API QueueForRelease();
RtStream(RtStreamPool *pool);
public:
virtual ~RtStream();
//---IBStream---------------------------------------
tresult PLUGIN_API read(void *buffer, int32 numBytes, int32 *numBytesRead) SMTG_OVERRIDE;
tresult PLUGIN_API write(void *buffer, int32 numBytes, int32 *numBytesWritten) SMTG_OVERRIDE;
tresult PLUGIN_API seek(int64 pos, int32 mode, int64 *result) SMTG_OVERRIDE;
tresult PLUGIN_API tell(int64 *pos) SMTG_OVERRIDE;
TSize getSize() const; ///< returns the current memory size
void setSize(TSize size); ///< set the memory size, a realloc will occur if memory already used
char *getData() const; ///< returns the memory pointer
char *detachData(); ///< returns the memory pointer and give up ownership
bool truncate(); ///< realloc to the current use memory size if needed
bool truncateToCursor(); ///< truncate memory at current cursor position
//------------------------------------------------------------------------
DECLARE_FUNKNOWN_METHODS
protected:
RtStreamPool *pool = nullptr;
RtStream *next = nullptr;
char *memory; // memory block
TSize memorySize; // size of the memory block
TSize size; // size of the stream
int64 cursor; // stream pointer
bool ownMemory; // stream has allocated memory itself
bool allocationError; // stream invalid
};
class RtStreamPool
{
public:
~RtStreamPool()
{
ReleaseStreams();
}
IRtStream *AllocateBStream()
{
ReleaseStreams();
return new RtStream(this);
}
void ReleaseStreams();
private:
friend class RtStream;
RtStream *freeStreamList = nullptr;
void QueueForRelease(RtStream *pStream)
{
pStream->next = freeStreamList;
freeStreamList = pStream;
}
};
inline void RtStream::QueueForRelease()
{
pool->QueueForRelease(this);
}
};
+1 -1
View File
@@ -19,7 +19,7 @@
#include "pch.h" #include "pch.h"
#include "catch.hpp" #include "catch.hpp"
#include <string> #include <string>
#include "Lv2Host.hpp" #include "PiPedalHost.hpp"
using namespace piddle; using namespace piddle;