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",
"includePath": [
"${default}",
"/usr/include/lilv-0"
"${workspaceFolder}",
"${workspaceFolder}/src",
"/usr/include/lilv-0",
"~/src/vst3sdk"
],
"compilerPath": "/usr/bin/gcc",
"cStandard": "c17",
+2 -3
View File
@@ -90,9 +90,8 @@
"span_ext": "cpp",
"string_span": "cpp",
"util": "cpp",
"charconv": "cpp",
"valarray": "cpp"
},
"cSpell.words": [
"Guitarix",
+1 -1
View File
@@ -265,7 +265,7 @@ GEM
thread_safe (0.3.6)
typhoeus (1.4.0)
ethon (>= 0.9.0)
tzinfo (1.2.9)
tzinfo (1.2.10)
thread_safe (~> 0.1)
unf (0.1.4)
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:maximum 100.0 ;
units:unit units:pc;
rdfs:comment "Attenuation of the first delay";
rdfs:comment "Attenuation of the first echo";
], [
a lv2:InputPort ,
@@ -92,7 +92,7 @@ A straighforward no-frills digital delay.
lv2:minimum 0.0 ;
lv2:maximum 100.0 ;
units:unit units:pc;
rdfs:comment "Attenuation of subequent delays";
rdfs:comment "Attenuation of subequent echoes";
],
[
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": "*",
"debug": true,
"max_upload_size": 1048576,
+18 -2
View File
@@ -228,6 +228,7 @@ export const LoadPluginDialog =
let gridWidth = width - this.margin_reserve;
let columns = Math.floor((gridWidth) / this.nominal_column_width);
if (columns < 1) columns = 1;
if (columns > 3) columns = 3;
return columns;
}
@@ -388,6 +389,14 @@ export const LoadPluginDialog =
// 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 {
if (!uiPlugin) return "";
if (uiPlugin.audio_inputs === 2 || uiPlugin.audio_outputs === 2) {
@@ -436,7 +445,13 @@ export const LoadPluginDialog =
for (let i = 0; i < plugins.length; ++i) {
let plugin = plugins[i];
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 (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" }} >
<PluginInfoDialog plugin_uri={this.state.selected_uri ?? ""} />
<Typography display='block' variant='body2' color="textPrimary" noWrap >
{this.info_string(selectedPlugin)}
{this.info_string(selectedPlugin)}
</Typography>
{this.vst3_indicator(selectedPlugin)}
<div style={{
color: isFavorite ? "#F80" : "#CCC", display: (this.state.selected_uri !== "" ? "block" : "none"),
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.port_group = input.port_group;
this.comment = input.comment;
this.is_bypass = input.is_bypass;
this.is_program_controller = input.is_program_controller;
return this;
}
@@ -75,12 +77,17 @@ export class Port implements Deserializable<Port> {
supports_time_position: boolean = false;
port_group: string = "";
comment: string = "";
is_bypass: boolean = false;
is_program_controller: boolean = false;
}
export class PortGroup {
deserialize(input: any): PortGroup {
this.symbol = input.symbol;
this.name = input.name;
this.parent_group = input.parent_group;
this.program_list_id = input.program_list_id ?? -1;
return this;
}
static deserialize_array(input: any) : PortGroup[] {
@@ -94,6 +101,8 @@ export class PortGroup {
symbol: string = "";
name: string = "";
parent_group: string = "";
program_list_id: number = -1;
};
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.port_group = input.port_group;
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;
if (this.is_bypass)
{
this.not_on_gui = true;
}
if (this.enumeration_property && this.scale_points.length === 2)
{
@@ -234,7 +252,7 @@ export class UiControl implements Deserializable<UiControl> {
} else {
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;
}
@@ -276,6 +294,10 @@ export class UiControl implements Deserializable<UiControl> {
port_group: string = "";
units: Units = Units.none;
comment: string = "";
is_bypass: boolean = true;
is_program_controller: boolean = true;
custom_units: string = "";
// Return the value of the closest scale_point.
clampSelectValue(value: number): number{
@@ -452,6 +474,7 @@ export class UiPlugin implements Deserializable<UiPlugin> {
this.description = input.description;
this.controls = UiControl.deserialize_array(input.controls);
this.port_groups = PortGroup.deserialize_array(input.port_groups);
this.is_vst3 = input.is_vst3;
return this;
}
@@ -501,5 +524,6 @@ export class UiPlugin implements Deserializable<UiPlugin> {
description: string = "";
controls: UiControl[] = [];
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.controlValues = ControlValue.deserializeArray(input.controlValues);
this.vstState = input.vstState ?? "";
return this;
}
deserialize(input: any): PedalBoardItem {
@@ -183,6 +184,7 @@ export class PedalBoardItem implements Deserializable<PedalBoardItem> {
pluginName?: string;
controlValues: ControlValue[] = ControlValue.EmptyArray;
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 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({
scrollContainer: {
@@ -210,10 +216,9 @@ class PedalLayout {
bounds: Rect = new Rect();
inputs: number = 0;
outputs: number = 0;
stereoInput: boolean = false;
stereoOutput: boolean = false;
numberOfInputs: number = 2;
numberOfOutputs: number = 2;
pedalItem?: PedalBoardItem;
// Split Layout only.
@@ -227,8 +232,8 @@ class PedalLayout {
let t: PedalLayout = new PedalLayout();
t.uri = START_PEDALBOARD_ITEM_URI;
t.iconUrl = TERMINAL_ICON_URL;
t.inputs = 0;
t.outputs = 1;
t.numberOfInputs = 0;
t.numberOfOutputs = 2;
return t;
}
static End(): PedalLayout {
@@ -236,8 +241,8 @@ class PedalLayout {
t.pluginType = PluginType.UtilityPlugin;
t.uri = END_PEDALBOARD_ITEM_URI;
t.iconUrl = TERMINAL_ICON_URL;
t.inputs = 2;
t.outputs = 0;
t.numberOfInputs = 2;
t.numberOfOutputs = 0;
return t;
}
constructor(model?: PiPedalModel, pedalItem?: PedalBoardItem) {
@@ -256,27 +261,27 @@ class PedalLayout {
this.topChildren = makeChain(model, splitter.topChain);
this.bottomChildren = makeChain(model, splitter.bottomChain);
this.inputs = 2;
this.outputs = 2;
this.numberOfInputs = 2;
this.numberOfOutputs = 2;
} else if (pedalItem.isEmpty()) {
this.pluginType = PluginType.UtilityPlugin;
this.iconUrl = EMPTY_ICON_URL;
this.inputs = 2;
this.outputs = 2;
this.numberOfInputs = 2;
this.numberOfOutputs = 2;
} else if (pedalItem.uri === START_PEDALBOARD_ITEM_URI) {
this.pluginType = PluginType.UtilityPlugin;
this.iconUrl = TERMINAL_ICON_URL;
this.inputs = 0;
this.outputs = PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length;
this.numberOfInputs = 0;
this.numberOfOutputs = PiPedalModelFactory.getInstance().jackSettings.get().inputAudioPorts.length;
} else if (pedalItem.uri === END_PEDALBOARD_ITEM_URI) {
this.pluginType = PluginType.UtilityPlugin;
this.iconUrl = TERMINAL_ICON_URL;
this.inputs = PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length;
this.outputs = 0;
this.numberOfInputs = PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length;
this.numberOfOutputs = 0;
}
else {
let uiPlugin = model.getUiPlugin(pedalItem.uri);
@@ -284,15 +289,15 @@ class PedalLayout {
this.pluginType = uiPlugin.plugin_type;
this.iconUrl = SelectIcon(uiPlugin.plugin_type,uiPlugin.uri);
this.name = uiPlugin.name;
this.inputs = uiPlugin.audio_inputs;
this.outputs = uiPlugin.audio_outputs;
this.numberOfInputs = Math.max(uiPlugin.audio_inputs,2);
this.numberOfOutputs = Math.max(uiPlugin.audio_outputs,2);
} else {
// default to empty plugin.
this.pluginType = PluginType.UtilityPlugin;
this.name = pedalItem.pluginName??"#error";
this.iconUrl = ERROR_ICON_URL;
this.inputs = 2;
this.outputs = 2;
this.numberOfInputs = 2;
this.numberOfOutputs = 2;
}
}
}
@@ -677,18 +682,18 @@ const PedalBoardView =
// let classes = this.props.classes;
let x_ = item.bounds.x + CELL_WIDTH / 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 svgPath = new SvgPathBuilder().moveTo(x_, y_).lineTo(x_ + CELL_WIDTH, y_).toString();
if (isStereo) {
if (numberOfOutputs === 2) {
output.push((
<path d={svgPath} stroke={color} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
));
output.push((
<path d={svgPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
));
} else {
} else if (numberOfOutputs === 1) {
output.push((
<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 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="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} />));
}
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="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} />));
}
@@ -741,6 +746,8 @@ const PedalBoardView =
let firstPathStereo: boolean;
let secondPathStereo: boolean;
let firstPathAbsent: boolean;
let secondPathAbsent: boolean;
let firstPathEnabled: boolean;
let secondPathEnabled: boolean;
let xTee: number;
@@ -753,11 +760,13 @@ const PedalBoardView =
// 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 hasThirdPath = item.stereoOutput && (!lastTop.stereoOutput) && (!lastBottom.stereoOutput);
let hasThirdPath = item.numberOfOutputs === 2 && (lastTop.numberOfOutputs !== 2) && (lastBottom.numberOfOutputs !== 2);
if (hasThirdPath) {
firstPathStereo = false;
secondPathStereo = false;
firstPathAbsent = lastTop.numberOfOutputs === 0 || item.numberOfOutputs === 0;
secondPathAbsent = lastBottom.numberOfOutputs === 0 || item.numberOfOutputs === 0;
xTee = xTee0 - monoAdjustment;
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();
firstPathEnabled = bottomEnabled;
secondPathEnabled = topEnabled;
} else if (bottomPathFirst || (topEnabled && lastTop.stereoOutput)) {
} else if (bottomPathFirst || (topEnabled && lastTop.numberOfOutputs === 2)) {
// draw the bottom path first.
firstPathStereo = item.stereoOutput && lastBottom.stereoOutput;
secondPathStereo = item.stereoOutput && lastTop.stereoOutput;
firstPathStereo = item.numberOfOutputs === 2 && lastBottom.numberOfOutputs === 2;
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;
firstPath = new SvgPathBuilder().moveTo(xBottom, yBottom).lineTo(xTee, yBottom).lineTo(xTee, y_).toString();
xTee = secondPathStereo ? xTee0 : xTee0 - monoAdjustment;
@@ -780,8 +792,10 @@ const PedalBoardView =
secondPathEnabled = topEnabled;
} else {
// draw the top path first.
firstPathStereo = item.stereoOutput && lastTop.stereoOutput;
secondPathStereo = item.stereoOutput && lastBottom.stereoOutput;
firstPathStereo = item.numberOfOutputs === 2 && lastTop.numberOfOutputs === 2;
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;
firstPath = new SvgPathBuilder().moveTo(xTop, yTop).lineTo(xTee, yTop).lineTo(xTee, y_).toString();
@@ -804,7 +818,7 @@ const PedalBoardView =
output.push((
<path d={firstPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
));
} else {
} else if (!firstPathAbsent) {
output.push((
<path d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STROKE_WIDTH} />
));
@@ -816,7 +830,7 @@ const PedalBoardView =
output.push((
<path d={secondPath} stroke="white" strokeWidth={SVG_STROKE_WIDTH} />
));
} else {
} else if (!secondPathAbsent) {
output.push((
<path d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STROKE_WIDTH} />
));
@@ -828,7 +842,7 @@ const PedalBoardView =
output.push((
<path d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
));
} else {
} else if (!firstPathAbsent) {
output.push((
<path d={firstPath} stroke={firstPathColor} strokeWidth={SVG_STROKE_WIDTH} />
));
@@ -837,7 +851,7 @@ const PedalBoardView =
output.push((
<path d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STEREO_STROKE_WIDTH} />
));
} else {
} else if (!secondPathAbsent) {
output.push((
<path d={secondPath} stroke={secondPathColor} strokeWidth={SVG_STROKE_WIDTH} />
));
@@ -1011,126 +1025,130 @@ const PedalBoardView =
}
return true;
}
canOutputStereo(item: PedalLayout): boolean {
getNumberOfInputs(item: PedalLayout): number {
if (item.pedalItem) {
let plugin = this.model.getUiPlugin(item.pedalItem.uri);
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.
this.markStereoForward(layoutChain, stereoInput);
this.markStereoForward(layoutChain, numberOfInputs);
// 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)
{
let item = layoutChain[i];
if (item.isSplitter())
{
if (!stereoOutput)
{
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;
item.numberOfOutputs = CalculateConnection(item.numberOfOutputs, numberOfOutputs)
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;
if (splitItem.getSplitType() !== SplitType.Lr)
{
item.stereoInput = false;
}
item.numberOfInputs = CalculateConnection(item.numberOfInputs, Math.max(topInputs,bottomInputs));
}
} else if (item.isEnd())
{
} else if (item.isStart())
{
if (!stereoOutput)
{
item.stereoOutput = false;
}
return item.stereoOutput;
item.numberOfOutputs = CalculateConnection(item.numberOfOutputs,numberOfOutputs);
return item.numberOfOutputs;
} else if (item.isEmpty()) {
if (!stereoOutput)
if (numberOfOutputs === 0)
{
item.stereoOutput = false;
item.stereoInput = false;
item.numberOfOutputs = 0;
item.numberOfInputs = CalculateConnection(item.numberOfInputs,2);
} else {
item.numberOfOutputs = CalculateConnection(item.numberOfOutputs,numberOfOutputs);
item.numberOfInputs = CalculateConnection(item.numberOfInputs,numberOfOutputs);
}
} else {
if (!stereoOutput)
{
item.stereoOutput = false;
}
item.numberOfOutputs = CalculateConnection(item.numberOfOutputs,numberOfOutputs);
}
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) {
return stereoInput;
return numberOfInputs;
}
for (let i = 0; i < layoutChain.length; ++i) {
let item = layoutChain[i];
if (item.isSplitter()) {
let splitter = item.pedalItem as PedalBoardSplitItem;
item.stereoInput = stereoInput;
item.numberOfInputs = numberOfInputs;
let isChainInputStereo = stereoInput;
let chainInputs = numberOfInputs;
if (splitter.getSplitType() === SplitType.Lr)
{
isChainInputStereo = false;
chainInputs = CalculateConnection(numberOfInputs,1);
}
let topStereo = this.markStereoForward(item.topChildren, isChainInputStereo);
let bottomStereo = this.markStereoForward(item.bottomChildren, isChainInputStereo);
let topOutputs = this.markStereoForward(item.topChildren, chainInputs);
let bottomOutputs = this.markStereoForward(item.bottomChildren, chainInputs);
if (splitter.getSplitType() === SplitType.Ab) {
if (splitter.isASelected()) {
item.stereoOutput = topStereo;
item.numberOfOutputs = topOutputs;
} else {
item.stereoOutput = bottomStereo;
item.numberOfOutputs = bottomOutputs;
}
} else if (splitter.getSplitType() === SplitType.Lr) {
item.stereoOutput = true;
item.numberOfOutputs = (topOutputs !== 0 && bottomOutputs !== 0) ? 2: 0;
} else {
item.stereoOutput = topStereo || bottomStereo;
item.numberOfOutputs = CalculateConnection(topOutputs,bottomOutputs);
}
} 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()) {
item.stereoInput = (PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length > 1) && stereoInput;
return item.stereoInput;
item.numberOfInputs =
CalculateConnection(
Math.min(PiPedalModelFactory.getInstance().jackSettings.get().outputAudioPorts.length,2),
numberOfInputs);
return item.numberOfInputs;
} else if (item.isEmpty()) {
item.stereoInput = stereoInput;
item.stereoOutput = stereoInput;
} else {
if (stereoInput && this.canOutputStereo(item)) {
item.stereoInput = true;
item.numberOfInputs = numberOfInputs;
if (numberOfInputs === 0)
{
item.numberOfOutputs = 2;
} 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[];
@@ -1145,7 +1163,7 @@ const PedalBoardView =
{
layoutChain.splice(0, 0, start);
layoutChain.splice(layoutChain.length, 0, end);
this.markStereoOutputs(layoutChain, true, true);
this.markStereoOutputs(layoutChain, 2,2);
}
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 { ObservableProperty } from './ObservableProperty';
import { PedalBoard, ControlValue } from './PedalBoard'
import { PedalBoard, PedalBoardItem, ControlValue } from './PedalBoard'
import PluginClass from './PluginClass';
import PiPedalSocket, { PiPedalMessageHeader } from './PiPedalSocket';
import { nullCast } from './Utility'
@@ -303,6 +303,13 @@ interface ControlChangedBody {
symbol: string;
value: number;
};
interface Vst3ControlChangedBody {
clientId: number;
instanceId: number;
symbol: string;
value: number;
state: string;
};
export interface FavoritesList {
[url: string]: boolean;
@@ -634,6 +641,16 @@ class PiPedalModelImpl implements PiPedalModel {
controlChangedBody.value,
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") {
let jackServerSettings = new JackServerSettings().deserialize(body);
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 {
@@ -1232,7 +1272,7 @@ class PiPedalModelImpl implements PiPedalModel {
clientId: this.clientId,
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 {
let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let result = newPedalBoard.deleteItem(instanceId);
if (result != null) {
this.pedalBoard.set(newPedalBoard);
@@ -1301,6 +1343,9 @@ class PiPedalModelImpl implements PiPedalModel {
if (fromInstanceId === toInstanceId) return;
let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(fromInstanceId);
if (fromItem === null) {
throw new PiPedalArgumentError("fromInstanceId not found.");
@@ -1317,6 +1362,9 @@ class PiPedalModelImpl implements PiPedalModel {
if (fromInstanceId === toInstanceId) return;
let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(fromInstanceId);
if (fromItem === null) {
throw new PiPedalArgumentError("fromInstanceId not found.");
@@ -1334,6 +1382,10 @@ class PiPedalModelImpl implements PiPedalModel {
let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(instanceId);
if (fromItem === null) {
throw new PiPedalArgumentError("fromInstanceId not found.");
@@ -1350,6 +1402,9 @@ class PiPedalModelImpl implements PiPedalModel {
let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(instanceId);
if (fromItem === null) {
throw new PiPedalArgumentError("fromInstanceId not found.");
@@ -1371,6 +1426,9 @@ class PiPedalModelImpl implements PiPedalModel {
let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let fromItem = newPedalBoard.getItem(fromInstanceId);
let toItem = newPedalBoard.getItem(toInstanceId);
if (fromItem === null) {
@@ -1389,6 +1447,9 @@ class PiPedalModelImpl implements PiPedalModel {
addPedalBoardItem(instanceId: number, append: boolean): number {
let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let item = newPedalBoard.getItem(instanceId);
if (item === null) {
throw new PiPedalArgumentError("instanceId not found.");
@@ -1402,6 +1463,8 @@ class PiPedalModelImpl implements PiPedalModel {
addPedalBoardSplitItem(instanceId: number, append: boolean): number {
let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let item = newPedalBoard.getItem(instanceId);
if (item === null) {
throw new PiPedalArgumentError("instanceId not found.");
@@ -1416,6 +1479,9 @@ class PiPedalModelImpl implements PiPedalModel {
setPedalBoardItemEmpty(instanceId: number): number {
let pedalBoard = this.pedalBoard.get();
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
let item = newPedalBoard.getItem(instanceId);
if (item === null) {
throw new PiPedalArgumentError("instanceId not found.");
@@ -1768,12 +1834,24 @@ class PiPedalModelImpl implements PiPedalModel {
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 {
let pedalBoard = this.pedalBoard.get();
if (!pedalBoard) {
throw new PiPedalStateError("Pedalboard not loaded.");
}
let newPedalBoard = pedalBoard.clone();
this.updateVst3State(newPedalBoard);
if (newPedalBoard.setMidiBinding(instanceId, midiBinding)) {
this.pedalBoard.set(newPedalBoard);
// notify the server.
+14 -8
View File
@@ -603,17 +603,23 @@ const PluginControl =
let uiControl = this.props.uiControl;
if (uiControl) {
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;
if (uiControl.is_logarithmic)
if (uiControl.min_value === uiControl.max_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);
value = uiControl.min_value;
} else
{
if (uiControl.is_logarithmic)
{
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;
}
+2 -1
View File
@@ -29,6 +29,7 @@
#include "AlsaDriver.hpp"
#include "JackServerSettings.hpp"
#include <thread>
#include "RtInversionGuard.hpp"
#include "CpuUse.hpp"
@@ -967,7 +968,7 @@ namespace pipedal
struct sched_param param;
memset(&param, 0, sizeof(param));
param.sched_priority = 79;
param.sched_priority = RT_THREAD_PRIORITY;
int result = sched_setscheduler(0, SCHED_RR, &param);
if (result == 0)
+279 -8
View File
@@ -7,6 +7,37 @@ set (USE_PCH 1)
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.
@@ -60,19 +91,27 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
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)
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)
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-fsanitize=address -static-libasan " )
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -static-libasan " )
endif()
elseif(CMAKE_BUILD_TYPE MATCHES RelWithDebInfo)
message(STATUS "RelWithgDebInfo build")
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wno-psabi -Werror" )
#set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "" )
else()
message(STATUS "Release build")
set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wno-psabi -Werror" )
#set (CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "" )
endif()
@@ -84,8 +123,19 @@ message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}" )
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
RtInversionGuard.hpp
CpuUse.hpp CpuUse.cpp
P2pConfigFiles.hpp
AvahiService.cpp AvahiService.hpp
@@ -104,7 +154,7 @@ set (PIPEDAL_SOURCES
WifiDirectConfigSettings.hpp WifiDirectConfigSettings.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
Lv2Log.hpp Lv2Log.cpp
PiPedalSocket.hpp PiPedalSocket.cpp
@@ -152,6 +202,7 @@ set (PIPEDAL_SOURCES
AudioDriver.hpp
AudioConfig.hpp
${VST3_SOURCES}
)
@@ -161,16 +212,25 @@ include_directories( ${pipedald_SOURCE_DIR}/. ../build/src)
set (PIPEDAL_INCLUDES
${LV2DEV_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}
# ${JACK_LIBRARIES}
${LIBNL3_LIBRARIES} )
${LIBNL3_LIBRARIES}
)
if (VST3_ENABLED)
set (PIPEDAL_LIBS vst3_lib ${PIPEDAL_LIBS})
endif()
##########################
add_library(libpipedald STATIC ${PIPEDAL_SOURCES})
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.
add_test(NAME BuildTest COMMAND pipedaltest "[Build]")
+11 -3
View File
@@ -23,11 +23,12 @@
namespace pipedal {
class RealtimeParameterRequest;
class RealtimeRingBufferWriter;
class IEffect {
public:
virtual ~IEffect() {}
virtual long GetInstanceId() const = 0;
virtual uint64_t GetInstanceId() const = 0;
virtual int GetControlIndex(const std::string&symbol) const = 0;
virtual void SetControl(int index, float value) = 0;
virtual float GetControlValue(int index) const = 0;
@@ -35,6 +36,7 @@ namespace pipedal {
virtual float GetOutputControlValue(int controlIndex) const = 0;
virtual int GetNumberOfInputAudioPorts() const = 0;
virtual int GetNumberOfOutputAudioPorts() const = 0;
virtual float *GetAudioInputBuffer(int index) const = 0;
@@ -43,13 +45,19 @@ namespace pipedal {
virtual void RequestParameter(LV2_URID uridUri) = 0;
virtual void GatherParameter(RealtimeParameterRequest*pRequest) = 0;
virtual std::string AtomToJson(uint8_t*pAtom) = 0;
virtual std::string GetAtomObjectType(uint8_t*pData) = 0;
//virtual std::string AtomToJson(uint8_t*pAtom) = 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 Activate() = 0;
virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter) = 0;
virtual void Deactivate() = 0;
virtual bool IsVst3() const = 0;
};
} //namespace
+239 -5
View File
@@ -106,6 +106,73 @@ static std::string GetGovernor()
class JackHostImpl : public JackHost, private AudioDriverHost
{
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;
inherit_priority_recursive_mutex mutex;
@@ -146,6 +213,30 @@ private:
std::atomic<std::chrono::system_clock::time_point> lastUnderrunTime =
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()
{
++this->underruns;
@@ -582,7 +673,6 @@ private:
}
}
public:
JackHostImpl(IHost *pHost)
@@ -592,7 +682,9 @@ public:
realtimeWriter(&this->outputRingBuffer),
hostReader(&this->outputRingBuffer),
hostWriter(&this->inputRingBuffer),
eventBufferUrids(pHost)
eventBufferUrids(pHost),
pHost(pHost),
uris(pHost)
{
#if JACK_HOST
@@ -758,7 +850,7 @@ public:
}
else
{
pRequest->jsonResponse = pEffect->AtomToJson(pRequest->response);
pRequest->jsonResponse = AtomToJson(pRequest->response);
}
}
pRequest->onJackRequestComplete(pRequest);
@@ -802,8 +894,8 @@ public:
IEffect *pEffect = currentPedalBoard->GetEffect(instanceId);
if (pEffect != nullptr &&this->pNotifyCallbacks && listenForAtomOutput)
{
std::string atomType = pEffect->GetAtomObjectType(&atomBuffer[0]);
auto json = pEffect->AtomToJson(&(atomBuffer[0]));
std::string atomType = GetAtomObjectType(&atomBuffer[0]);
auto json = AtomToJson(&(atomBuffer[0]));
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)
{
+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)
{
if (GetNumberOfInputs() > 1)
if (GetNumberOfInputAudioPorts() > 1)
{
SetAudioInputBuffer(0, left);
SetAudioInputBuffer(1, left);
@@ -229,7 +229,7 @@ void Lv2Effect::SetAudioInputBuffer(float *left)
void Lv2Effect::SetAudioInputBuffers(float *left, float *right)
{
if (GetNumberOfInputs() == 1)
if (GetNumberOfInputAudioPorts() == 1)
{
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.
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,
@@ -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
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "PedalBoard.hpp"
#include <lilv/lilv.h>
#include "BufferPool.hpp"
@@ -82,7 +82,6 @@ namespace pipedal
LV2_Atom_Forge outputForgeRt;
void WriteAtom(json_writer &writer, LV2_Atom*pAtom);
class Uris
{
@@ -167,9 +166,10 @@ namespace pipedal
void BypassTo(float value);
virtual void RequestParameter(LV2_URID uridUri);
virtual void GatherParameter(RealtimeParameterRequest*pRequest);
virtual bool IsVst3() const { return false; }
virtual void RelayOutputMessages(uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter);
virtual uint8_t*GetAtomInputBuffer() {
@@ -182,10 +182,6 @@ namespace pipedal
return (uint8_t*)this->outputAtomBuffers[0];
}
virtual std::string AtomToJson(uint8_t*pAtom);
virtual std::string GetAtomObjectType(uint8_t*pData);
public:
@@ -196,29 +192,30 @@ namespace pipedal
~Lv2Effect();
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 GetNumberOfOutputAudioPorts() const { return outputAudioPortIndices.size(); }
int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); }
int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); }
int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); }
int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); }
virtual int GetNumberOfInputAtomPorts() const { return inputAtomPortIndices.size(); }
virtual int GetNumberOfOutputAtomPorts() const { return outputAtomPortIndices.size(); }
virtual int GetNumberOfMidiInputPorts() const { return inputMidiPortIndices.size(); }
virtual int GetNumberOfMidiOutputPorts() const { return outputMidiPortIndices.size(); }
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);
void SetAudioInputBuffers(float *left, float *right);
virtual void SetAudioInputBuffer(float *buffer);
virtual void SetAudioInputBuffers(float *left, float *right);
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);
void *GetAtomInputBuffer(int index) const { return this->inputAtomBuffers[index]; }
virtual void SetAtomInputBuffer(int index, void *buffer) { this->inputAtomBuffers[index] = (char*)buffer;}
virtual void *GetAtomInputBuffer(int index) const { return this->inputAtomBuffers[index]; }
void SetAtomOutputBuffer(int index, void *buffer);
void *GetAtomOutputBuffer(int index) const { return this->outputAtomBuffers[index]; }
virtual void SetAtomOutputBuffer(int index, void *buffer) { this->outputAtomBuffers[index] = (char*)buffer; }
virtual void *GetAtomOutputBuffer(int index) const { return this->outputAtomBuffers[index]; }
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();
void Run(uint32_t samples, uint64_t instanceId,RealtimeRingBufferWriter *realtimeRingBufferWriter);
void Deactivate();
virtual void Activate();
virtual void Run(uint32_t samples, RealtimeRingBufferWriter *realtimeRingBufferWriter);
virtual void Deactivate();
};
} // namespace
+1 -1
View File
@@ -19,7 +19,7 @@
#include "pch.h"
#include "Lv2EventBufferWriter.hpp"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
using namespace pipedal;
+3 -3
View File
@@ -24,17 +24,17 @@
#include <string>
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "MemDebug.hpp"
using namespace pipedal;
TEST_CASE( "Lv2Host memory leak", "[lv2host_leak][Build][Dev]" ) {
TEST_CASE( "PiPedalHost memory leak", "[lv2host_leak][Build][Dev]" ) {
MemStats initialMemory = GetMemStats();
{
Lv2Host host;
PiPedalHost host;
host.Load("/usr/lib/lv2:/usr/local/lib/lv2:/usr/modep/lv2");
}
+5 -4
View File
@@ -21,6 +21,7 @@
#include "Lv2PedalBoard.hpp"
#include "Lv2Effect.hpp"
#include "SplitEffect.hpp"
#include "RingBufferReader.hpp"
#include "VuUpdate.hpp"
@@ -57,7 +58,7 @@ int Lv2PedalBoard::GetControlIndex(uint64_t instanceId, const std::string &symbo
return -1;
}
std::vector<float *> Lv2PedalBoard::PrepareItems(
const std::vector<PedalBoardItem> &items,
std::vector<PedalBoardItem> &items,
std::vector<float *> inputBuffers)
{
for (int i = 0; i < items.size(); ++i)
@@ -142,9 +143,9 @@ std::vector<float *> Lv2PedalBoard::PrepareItems(
}
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;
}
void Lv2PedalBoard::Prepare(IHost *pHost, const PedalBoard &pedalBoard)
void Lv2PedalBoard::Prepare(IHost *pHost, PedalBoard &pedalBoard)
{
this->pHost = pHost;
+5 -3
View File
@@ -19,7 +19,7 @@
#pragma once
#include "PedalBoard.hpp"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "Lv2Effect.hpp"
#include "BufferPool.hpp"
#include <functional>
@@ -81,7 +81,7 @@ class Lv2PedalBoard {
std::vector<float*> PrepareItems(
const std::vector<PedalBoardItem> & items,
std::vector<PedalBoardItem> & items,
std::vector<float*> inputBuffers
);
@@ -95,7 +95,9 @@ public:
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) {
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> bottomChain_;
std::vector<MidiBinding> midiBindings_;
std::string vstState_;
public:
ControlValue*GetControlValue(const std::string&symbol);
GETTER_SETTER(instanceId)
GETTER_SETTER_REF(uri)
GETTER_SETTER_REF(vstState);
GETTER_SETTER_REF(pluginName)
GETTER_SETTER(isEnabled)
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, accessPointGateway)
JSON_MAP_REFERENCE(PiPedalConfiguration, accessPointServerAddress)
JSON_MAP_REFERENCE(PiPedalConfiguration, isVst3Enabled)
JSON_MAP_END()
+2 -1
View File
@@ -47,9 +47,10 @@ private:
uint64_t maxUploadSize_ = 1024*1024;
std::string accessPointGateway_;
std::string accessPointServerAddress_;
bool isVst3Enabled_ = true;
public:
bool IsVst3Enabled() const { return isVst3Enabled_; }
std::filesystem::path GetConfigFilePath() const {
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.
#include "pch.h"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include <lilv/lilv.h>
#include <stdexcept>
#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_DESIGNATION = "http://lv2plug.in/ns/lv2core#Designation";
class Lv2Host::Urids
class PiPedalHost::Urids
{
public:
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);
display_priority_uri = lilv_new_uri(pWorld, LV2_PORT_DISPLAY_PRIORITY);
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);
}
void Lv2Host::LilvUris::Free()
void PiPedalHost::LilvUris::Free()
{
rdfsComment.Free();
logarithic_uri.Free();
@@ -242,7 +250,7 @@ public:
}
};
Lv2Host::Lv2Host()
PiPedalHost::PiPedalHost()
{
pWorld = nullptr;
@@ -260,7 +268,7 @@ Lv2Host::Lv2Host()
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();
if (configuration.isValid())
@@ -272,7 +280,7 @@ void Lv2Host::OnConfigurationChanged(const JackConfiguration &configuration, con
}
}
Lv2Host::~Lv2Host()
PiPedalHost::~PiPedalHost()
{
delete[] lv2Features;
lilvUris.Free();
@@ -280,7 +288,7 @@ Lv2Host::~Lv2Host()
delete urids;
}
void Lv2Host::free_world()
void PiPedalHost::free_world()
{
if (pWorld)
{
@@ -288,7 +296,7 @@ void Lv2Host::free_world()
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);
if (it == this->classesMap.end())
@@ -296,7 +304,7 @@ std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const std::string &uri)
return (*it).second;
}
void Lv2Host::AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass)
void PiPedalHost::AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass)
{
this->classesMap[pluginClass->uri()] = pluginClass;
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();
}
}
void Lv2Host::LoadPluginClassesFromJson(std::filesystem::path jsonFile)
void PiPedalHost::LoadPluginClassesFromJson(std::filesystem::path jsonFile)
{
std::ifstream f(jsonFile);
json_reader reader(f);
@@ -320,7 +328,7 @@ void Lv2Host::LoadPluginClassesFromJson(std::filesystem::path jsonFile)
MAP_CHECK();
}
std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const LilvPluginClass *pClass)
std::shared_ptr<Lv2PluginClass> PiPedalHost::GetPluginClass(const LilvPluginClass *pClass)
{
MAP_CHECK();
@@ -328,7 +336,7 @@ std::shared_ptr<Lv2PluginClass> Lv2Host::GetPluginClass(const LilvPluginClass *p
std::shared_ptr<Lv2PluginClass> pResult = this->classesMap[uri];
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));
auto t = this->classesMap.find(uri);
@@ -348,7 +356,7 @@ std::shared_ptr<Lv2PluginClass> Lv2Host::MakePluginClass(const LilvPluginClass *
return result;
}
void Lv2Host::LoadPluginClassesFromLilv()
void PiPedalHost::LoadPluginClassesFromLilv()
{
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();
@@ -390,13 +398,14 @@ void Lv2Host::Load(const char *lv2Path)
free_world();
Lv2Log::info("Scanning for LV2 Plugins");
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();
{
lilv_world_load_all(pWorld);
}
lilvUris.Initialize(pWorld);
@@ -443,7 +452,7 @@ void Lv2Host::Load(const char *lv2Path)
}
auto messages = stdoutCapture.GetOutputLines();
for (const std::string & s: messages)
for (const std::string &s : messages)
{
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
{
@@ -532,10 +568,10 @@ static std::vector<std::string> nodeAsStringArray(const LilvNodes *nodes)
return result;
}
const char *Lv2Host::RDFS_COMMENT_URI = "http://www.w3.org/2000/01/rdf-schema#"
"comment";
const char *PiPedalHost::RDFS_COMMENT_URI = "http://www.w3.org/2000/01/rdf-schema#"
"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());
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();
}
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);
bool result = false;
@@ -559,7 +595,7 @@ bool Lv2PluginInfo::HasFactoryPresets(Lv2Host *lv2Host, const LilvPlugin *plugin
return result;
}
Lv2PluginInfo::Lv2PluginInfo(Lv2Host *lv2Host, const LilvPlugin *pPlugin)
Lv2PluginInfo::Lv2PluginInfo(PiPedalHost *lv2Host, const LilvPlugin *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);
}
@@ -686,7 +722,7 @@ static bool scale_points_sort_compare(const Lv2ScalePoint &v1, const Lv2ScalePoi
{
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;
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);
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);
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_));
}
Lv2PluginClasses Lv2Host::GetPluginPortClass(const LilvPlugin *lilvPlugin, const LilvPort *lilvPort)
Lv2PluginClasses PiPedalHost::GetPluginPortClass(const LilvPlugin *lilvPlugin, const LilvPort *lilvPort)
{
std::vector<std::string> result;
const LilvNodes *nodes = lilv_port_get_classes(lilvPlugin, lilvPort);
@@ -832,7 +868,7 @@ bool Lv2PluginClass::is_a(const std::string &classUri) const
}
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);
for (auto i : classes_)
@@ -845,7 +881,7 @@ bool Lv2PluginClasses::is_a(Lv2Host *lv2Plugins, const char *classUri) const
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)
return true;
@@ -857,7 +893,7 @@ bool Lv2Host::is_a(const std::string &class_, const std::string &target_class)
return false;
}
Lv2PluginUiInfo::Lv2PluginUiInfo(Lv2Host *pHost, const Lv2PluginInfo *plugin)
Lv2PluginUiInfo::Lv2PluginUiInfo(PiPedalHost *pHost, const Lv2PluginInfo *plugin)
: uri_(plugin->uri()),
name_(plugin->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);
}
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)
{
@@ -936,7 +972,7 @@ std::shared_ptr<Lv2PluginInfo> Lv2Host::GetPluginInfo(const std::string &uri) co
return nullptr;
}
Lv2PedalBoard *Lv2Host::CreateLv2PedalBoard(const PedalBoard &pedalBoard)
Lv2PedalBoard *PiPedalHost::CreateLv2PedalBoard(PedalBoard &pedalBoard)
{
Lv2PedalBoard *pPedalBoard = new Lv2PedalBoard();
try
@@ -953,18 +989,18 @@ Lv2PedalBoard *Lv2Host::CreateLv2PedalBoard(const PedalBoard &pedalBoard)
struct StateCallbackData
{
Lv2Host *pHost;
PiPedalHost *pHost;
std::vector<ControlValue> *pResult;
};
void Lv2Host::fn_LilvSetPortValueFunc(const char *port_symbol,
void *user_data,
const void *value,
uint32_t size,
uint32_t type)
void PiPedalHost::fn_LilvSetPortValueFunc(const char *port_symbol,
void *user_data,
const void *value,
uint32_t size,
uint32_t type)
{
StateCallbackData *pData = (StateCallbackData *)user_data;
Lv2Host *pHost = pData->pHost;
PiPedalHost *pHost = pData->pHost;
std::vector<ControlValue> *pResult = pData->pResult;
LV2_URID valueType = (LV2_URID)type;
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));
}
std::vector<ControlValue> Lv2Host::LoadFactoryPluginPreset(
std::vector<ControlValue> PiPedalHost::LoadFactoryPluginPreset(
PedalBoardItem *pedalBoardItem, const std::string &presetUri)
{
@@ -1032,15 +1068,15 @@ std::vector<ControlValue> Lv2Host::LoadFactoryPluginPreset(
struct PresetCallbackState
{
Lv2Host *pHost;
PiPedalHost *pHost;
std::map<std::string, float> *values;
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);
Lv2Host *pHost = pState->pHost;
PiPedalHost *pHost = pState->pHost;
if (type == pHost->urids->atom_Float)
{
@@ -1051,7 +1087,7 @@ void Lv2Host::PortValueCallback(const char *symbol, void *user_data, const void
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);
@@ -1093,17 +1129,38 @@ PluginPresets Lv2Host::GetFactoryPluginPresets(const std::string &pluginUri)
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());
if (!info)
return nullptr;
#endif
}
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;
@@ -1131,47 +1188,45 @@ json_map::storage_type<Lv2ScalePoint> Lv2ScalePoint::jmap{{
json_map::reference("label", &Lv2ScalePoint::label_),
}};
json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{
json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("symbol", &Lv2PortInfo::symbol_),
json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("name", &Lv2PortInfo::name_),
json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("symbol", &Lv2PortInfo::symbol_),
json_map::reference("index", &Lv2PortInfo::index_),
json_map::reference("name", &Lv2PortInfo::name_),
json_map::reference("min_value", &Lv2PortInfo::min_value_),
json_map::reference("max_value", &Lv2PortInfo::max_value_),
json_map::reference("default_value", &Lv2PortInfo::default_value_),
json_map::reference("classes", &Lv2PortInfo::classes_),
json_map::reference("min_value", &Lv2PortInfo::min_value_),
json_map::reference("max_value", &Lv2PortInfo::max_value_),
json_map::reference("default_value", &Lv2PortInfo::default_value_),
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_output", &Lv2PortInfo::is_output_),
json_map::reference("is_input", &Lv2PortInfo::is_input_),
json_map::reference("is_output", &Lv2PortInfo::is_output_),
json_map::reference("is_control_port", &Lv2PortInfo::is_control_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_cv_port", &Lv2PortInfo::is_cv_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_atom_port", &Lv2PortInfo::is_audio_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_time_position", &Lv2PortInfo::supports_time_position_),
json_map::reference("port_group", &Lv2PortInfo::port_group_),
json_map::reference("is_logarithmic", &Lv2PortInfo::is_logarithmic_),
json_map::reference("supports_midi", &Lv2PortInfo::supports_midi_),
json_map::reference("supports_time_position", &Lv2PortInfo::supports_time_position_),
json_map::reference("port_group", &Lv2PortInfo::port_group_),
json_map::reference("is_logarithmic", &Lv2PortInfo::is_logarithmic_),
MAP_REF(Lv2PortInfo, is_logarithmic),
MAP_REF(Lv2PortInfo, display_priority),
MAP_REF(Lv2PortInfo, range_steps),
MAP_REF(Lv2PortInfo, trigger),
MAP_REF(Lv2PortInfo, integer_property),
MAP_REF(Lv2PortInfo, enumeration_property),
MAP_REF(Lv2PortInfo, toggled_property),
MAP_REF(Lv2PortInfo, not_on_gui),
MAP_REF(Lv2PortInfo, buffer_type),
MAP_REF(Lv2PortInfo, port_group),
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
MAP_REF(Lv2PortInfo, comment)
}};
MAP_REF(Lv2PortInfo, is_logarithmic),
MAP_REF(Lv2PortInfo, display_priority),
MAP_REF(Lv2PortInfo, range_steps),
MAP_REF(Lv2PortInfo, trigger),
MAP_REF(Lv2PortInfo, integer_property),
MAP_REF(Lv2PortInfo, enumeration_property),
MAP_REF(Lv2PortInfo, toggled_property),
MAP_REF(Lv2PortInfo, not_on_gui),
MAP_REF(Lv2PortInfo, buffer_type),
MAP_REF(Lv2PortInfo, port_group),
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
MAP_REF(Lv2PortInfo, comment)}};
json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{
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::storage_type<Lv2PluginUiInfo> Lv2PluginUiInfo::jmap{{
json_map::reference("uri", &Lv2PluginUiInfo::uri_),
json_map::reference("name", &Lv2PluginUiInfo::name_),
json_map::enum_reference("plugin_type", &Lv2PluginUiInfo::plugin_type_, get_plugin_type_enum_converter()),
json_map::reference("plugin_display_type", &Lv2PluginUiInfo::plugin_display_type_),
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::storage_type<Lv2PluginClass> Lv2PluginClass::jmap{{
MAP_REF(Lv2PluginClass, uri),
MAP_REF(Lv2PluginClass, display_name),
MAP_REF(Lv2PluginClass, parent_uri),
json_map::enum_reference("plugin_type", &Lv2PluginClass::plugin_type_, get_plugin_type_enum_converter()),
MAP_REF(Lv2PluginClass, children),
}};
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{{
@@ -1234,18 +1287,26 @@ json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{
json_map::enum_reference("units", &Lv2PluginUiControlPort::units_, get_units_enum_converter()),
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{{
MAP_REF(Lv2PluginClass, uri),
MAP_REF(Lv2PluginClass, display_name),
MAP_REF(Lv2PluginClass, parent_uri),
json_map::enum_reference("plugin_type", &Lv2PluginClass::plugin_type_, get_plugin_type_enum_converter()),
MAP_REF(Lv2PluginClass, children),
}};
json_map::storage_type<Lv2PluginUiInfo> Lv2PluginUiInfo::jmap{{
json_map::reference("uri", &Lv2PluginUiInfo::uri_),
json_map::reference("name", &Lv2PluginUiInfo::name_),
json_map::enum_reference("plugin_type", &Lv2PluginUiInfo::plugin_type_, get_plugin_type_enum_converter()),
json_map::reference("plugin_display_type", &Lv2PluginUiInfo::plugin_display_type_),
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 <filesystem>
#include <cmath>
#include <string>
#include "lv2.h"
#include "Units.hpp"
#include "PluginPreset.hpp"
#include "IEffect.hpp"
#include "PiPedalConfiguration.hpp"
namespace pipedal {
@@ -42,19 +46,23 @@ namespace pipedal {
// forward declarations
class Lv2Effect;
class Lv2PedalBoard;
class Lv2Host;
class PiPedalHost;
class JackConfiguration;
class JackChannelSelection;
#ifndef LV2_PROPERTY_GETSET
#define LV2_PROPERTY_GETSET(name) \
const decltype(name##_) & name() const { return name##_; }; \
decltype(name##_) & name() { return name##_; }; \
void name(const decltype(name##_) &value) { name##_ = value; };
#endif
#ifndef LV2_PROPERTY_GETSET_SCALAR
#define LV2_PROPERTY_GETSET_SCALAR(name) \
decltype(name##_) name() const { return name##_;}; \
void name(decltype(name##_) value) { name##_ = value; };
#endif
@@ -62,7 +70,7 @@ class JackChannelSelection;
class Lv2PluginClass {
public:
friend class Lv2Host;
friend class PiPedalHost;
private:
Lv2PluginClass* parent_ = nullptr; // NOT SERIALIZED!
std::string parent_uri_;
@@ -72,7 +80,7 @@ private:
std::vector<std::shared_ptr<Lv2PluginClass>> children_;
friend class ::pipedal::Lv2Host;
friend class ::pipedal::PiPedalHost;
// hide copy constructor.
Lv2PluginClass(const Lv2PluginClass &other)
{
@@ -135,7 +143,7 @@ public:
{
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;
@@ -171,7 +179,7 @@ enum class Lv2BufferType {
class Lv2PortInfo {
public:
Lv2PortInfo(Lv2Host*lv2Host,const LilvPlugin*pPlugin,const LilvPort *pPort);
Lv2PortInfo(PiPedalHost*lv2Host,const LilvPlugin*pPlugin,const LilvPort *pPort);
private:
friend class Lv2PluginInfo;
@@ -276,7 +284,7 @@ public:
public:
Lv2PortInfo() { }
~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;
@@ -294,19 +302,19 @@ public:
LV2_PROPERTY_GETSET(name);
Lv2PortGroup() { }
Lv2PortGroup(Lv2Host*lv2Host,const std::string &groupUri);
Lv2PortGroup(PiPedalHost*lv2Host,const std::string &groupUri);
static json_map::storage_type<Lv2PortGroup> jmap;
};
class Lv2PluginInfo {
private:
friend class Lv2Host;
friend class PiPedalHost;
public:
Lv2PluginInfo(Lv2Host*lv2Host,const LilvPlugin*);
Lv2PluginInfo(PiPedalHost*lv2Host,const LilvPlugin*);
Lv2PluginInfo() { }
private:
bool HasFactoryPresets(Lv2Host*lv2Host, const LilvPlugin*plugin);
bool HasFactoryPresets(PiPedalHost*lv2Host, const LilvPlugin*plugin);
std::string uri_;
std::string name_;
std::string plugin_class_;
@@ -399,139 +407,151 @@ public:
};
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())
class Lv2PluginUiPortGroup
{
// 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)
private:
std::string symbol_;
std::string name_;
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;
}
}
}
}
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:
static json_map::storage_type<Lv2PluginUiPortGroup> jmap;
};
public:
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())
class Lv2PluginUiControlPort
{
}
public:
static json_map::storage_type<Lv2PluginUiPortGroup> jmap;
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.
// 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 {
public:
Lv2PluginUiInfo() { }
Lv2PluginUiInfo(Lv2Host *pPlugins,const Lv2PluginInfo *plugin);
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_;
bool is_bypass_ = false;
bool is_program_controller_ = false;
std::string custom_units_;
private:
std::string uri_;
std::string name_;
std::string author_name_;
std::string author_homepage_;
PluginType plugin_type_;
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_;
public:
LV2_PROPERTY_GETSET(symbol);
LV2_PROPERTY_GETSET_SCALAR(index);
LV2_PROPERTY_GETSET(name);
LV2_PROPERTY_GETSET(port_group);
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);
LV2_PROPERTY_GETSET_SCALAR(is_bypass);
LV2_PROPERTY_GETSET_SCALAR(is_program_controller);
LV2_PROPERTY_GETSET(custom_units);
std::vector<Lv2PluginUiControlPort> controls_;
std::vector<Lv2PluginUiPortGroup> port_groups_;
public:
static json_map::storage_type<Lv2PluginUiControlPort> jmap;
};
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(port_groups)
class Lv2PluginUiInfo
{
public:
Lv2PluginUiInfo() {}
Lv2PluginUiInfo(PiPedalHost *pPlugins, const Lv2PluginInfo *plugin);
private:
std::string uri_;
std::string name_;
std::string author_name_;
std::string author_homepage_;
PluginType plugin_type_;
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 {
public:
@@ -552,7 +572,7 @@ public:
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;}
};
class Lv2Host: private IHost {
}
#if ENABLE_VST3
#include "vst3/Vst3Host.hpp"
#endif
namespace pipedal{
class PiPedalHost: private IHost {
private:
#if ENABLE_VST3
Vst3Host::Ptr vst3Host;
#endif
static const char *RDFS_COMMENT_URI;
class LilvUris {
public:
@@ -619,6 +655,8 @@ private:
};
bool vst3Enabled = true;
LilvUris lilvUris;
LilvNode *get_comment(const std::string &uri);
@@ -631,6 +669,8 @@ private:
int numberOfAudioOutputChannels = 2;
double sampleRate = 0;
std::string vst3CachePath;
LV2_Feature*const*lv2Features = nullptr;
MapFeature mapFeature;
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);
virtual Lv2Effect*CreateEffect(const PedalBoardItem &pedalBoardItem);
virtual IEffect*CreateEffect(PedalBoardItem &pedalBoardItem);
void LoadPluginClassesFromLilv();
void AddJsonClassesToMap(std::shared_ptr<Lv2PluginClass> pluginClass);
public:
Lv2Host();
virtual ~Lv2Host();
PiPedalHost();
void SetConfiguration(const PiPedalConfiguration&configuration);
virtual ~PiPedalHost();
IHost* asIHost() { return this;}
virtual Lv2PedalBoard * CreateLv2PedalBoard(const PedalBoard& pedalBoard);
virtual Lv2PedalBoard * CreateLv2PedalBoard(PedalBoard& pedalBoard);
void setSampleRate(double sampleRate)
{
@@ -719,7 +762,7 @@ public:
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) {
return this->mapFeature.GetUrid(uri);
@@ -736,5 +779,5 @@ public:
};
#undef LV2_PROPERTY_GETSET
#undef LV2_PROPERTY_GETSET_SCALAR
} // namespace pipedal.
+86 -24
View File
@@ -43,9 +43,18 @@ T &constMutex(const 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()
@@ -119,6 +128,7 @@ PiPedalModel::~PiPedalModel()
void PiPedalModel::Init(const PiPedalConfiguration &configuration)
{
this->configuration = configuration;
lv2Host.SetConfiguration(configuration);
storage.SetConfigRoot(configuration.GetDocRoot());
storage.SetDataRoot(configuration.GetLocalStoragePath());
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)
{
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)
@@ -291,6 +311,8 @@ void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::
std::lock_guard<std::recursive_mutex> guard(mutex);
PreviewControl(clientId, 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)
@@ -300,9 +322,11 @@ void PiPedalModel::SetControl(int64_t clientId, int64_t pedalItemId, const std::
t[i] = this->subscribers[i];
}
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;
@@ -379,6 +403,23 @@ void PiPedalModel::SetPedalBoard(int64_t clientId, PedalBoard &pedalBoard)
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)
{
@@ -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)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
UpdateVst3Settings(this->pedalBoard);
storage.SaveCurrentPreset(this->pedalBoard);
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};
auto pedalboard = this->pedalBoard;
UpdateVst3Settings(pedalBoard);
pedalboard.name(name);
int64_t result = storage.SaveCurrentPresetAs(pedalboard, name, saveAfterInstanceId);
FirePresetsChanged(clientId);
@@ -697,14 +764,13 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
}
}
static std::string GetP2pdName()
{
std::string name = "/etc/pipedal/config/pipedal_p2pd.conf";
std::string result;
if (ConfigUtil::GetConfigLine(name,"p2p_device_name",&result))
if (ConfigUtil::GetConfigLine(name, "p2p_device_name", &result))
{
return result;
}
@@ -718,17 +784,15 @@ void PiPedalModel::UpdateDnsSd()
ServiceConfiguration deviceIdFile;
deviceIdFile.Load();
std::string p2pdName = GetP2pdName();
if (p2pdName != "") {
if (p2pdName != "")
{
deviceIdFile.deviceName = p2pdName;
}
if (deviceIdFile.deviceName != "" && deviceIdFile.uuid != "")
{
avahiService.Announce(webPort, deviceIdFile.deviceName, deviceIdFile.uuid, "pipedal");
}
else
{
@@ -820,16 +884,16 @@ void PiPedalModel::RestartAudio()
this->jackHost->Close();
}
// restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack.
// restarting is a bit dodgy. It was impossible with Jack, but
// 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
// the process.
//...
// for the meantime, just rely on the fact that the admin service will restart
// the process.
//...
// do a complete reload.
// do a complete reload.
this->jackHost->SetPedalBoard(nullptr);
@@ -839,14 +903,14 @@ void PiPedalModel::RestartAudio()
{
JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration);
selection = selection.RemoveInvalidChannels(jackConfiguration);
} else {
}
else
{
jackConfiguration.SetErrorStatus("Error");
}
FireJackConfigurationChanged(jackConfiguration);
if (!jackServerSettings.IsValid() || !jackConfiguration.isValid())
{
Lv2Log::error("Audio configuration not valid.");
@@ -1223,12 +1287,11 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
}
delete[] t;
#if ALSA_HOST
storage.SetJackServerSettings(jackServerSettings);
FireJackConfigurationChanged(this->jackConfiguration);
guard.unlock();
RestartAudio();
@@ -1280,7 +1343,6 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
#endif
}
});
}
#endif
}
+7 -3
View File
@@ -19,7 +19,7 @@
#pragma once
#include <mutex>
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "GovernorSettings.hpp"
#include "PedalBoard.hpp"
#include "Storage.hpp"
@@ -52,6 +52,7 @@ public:
virtual int64_t GetClientId() = 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 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 OnPresetsChanged(int64_t clientId, const PresetIndex&presets) = 0;
virtual void OnPluginPresetsChanged(const std::string&pluginUri) = 0;
@@ -104,7 +105,7 @@ private:
std::vector<AtomOutputListener> atomOutputListeners;
JackServerSettings jackServerSettings;
Lv2Host lv2Host;
PiPedalHost lv2Host;
PedalBoard pedalBoard;
Storage storage;
bool hasPresetChanged = false;
@@ -155,6 +156,7 @@ private: // IJackHostCallbacks
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl);
virtual void OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson);
void UpdateVst3Settings(PedalBoard&pedalBoard);
PiPedalConfiguration configuration;
public:
@@ -173,7 +175,7 @@ public:
void LoadLv2PluginInfo();
void Load();
const Lv2Host& GetLv2Host() const { return lv2Host; }
const PiPedalHost& GetLv2Host() const { return lv2Host; }
PedalBoard GetCurrentPedalBoardCopy()
{
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 PreviewControl(int64_t clientId,int64_t pedalItemId, const std::string&symbol,float value);
void SetPedalBoard(int64_t clientId,PedalBoard &pedalBoard);
void UpdateCurrentPedalBoard(int64_t clientId, PedalBoard &pedalBoard);
void GetPresets(PresetIndex*pResult);
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_END()
class SetCurrentPedalBoardBody
class UpdateCurrentPedalBoardBody
{
public:
int64_t clientId_ = -1;
PedalBoard pedalBoard_;
DECLARE_JSON_MAP(SetCurrentPedalBoardBody);
DECLARE_JSON_MAP(UpdateCurrentPedalBoardBody);
};
JSON_MAP_BEGIN(SetCurrentPedalBoardBody)
JSON_MAP_REFERENCE(SetCurrentPedalBoardBody, clientId)
JSON_MAP_REFERENCE(SetCurrentPedalBoardBody, pedalBoard)
JSON_MAP_BEGIN(UpdateCurrentPedalBoardBody)
JSON_MAP_REFERENCE(UpdateCurrentPedalBoardBody, clientId)
JSON_MAP_REFERENCE(UpdateCurrentPedalBoardBody, pedalBoard)
JSON_MAP_END()
class ChannelSelectionChangedBody
@@ -335,6 +335,26 @@ JSON_MAP_REFERENCE(ControlChangedBody, symbol)
JSON_MAP_REFERENCE(ControlChangedBody, value)
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
{
private:
@@ -940,13 +960,13 @@ public:
pReader->read(&body);
model.SetPedalBoardItemEnable(body.clientId_, body.instanceId_, body.enabled_);
}
else if (message == "setCurrentPedalBoard")
else if (message == "updateCurrentPedalBoard")
{
{
SetCurrentPedalBoardBody body;
UpdateCurrentPedalBoardBody body;
pReader->read(&body);
this->model.SetPedalBoard(body.clientId_, body.pedalBoard_);
this->model.UpdateCurrentPedalBoard(body.clientId_, body.pedalBoard_);
}
}
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)
{
ControlChangedBody body;
@@ -1544,7 +1575,7 @@ public:
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard)
{
SetCurrentPedalBoardBody body;
UpdateCurrentPedalBoardBody body;
body.clientId_ = clientId;
body.pedalBoard_ = pedalBoard;
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 "SplitEffect.hpp"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include "PedalBoard.hpp"
using namespace pipedal;
+16 -1
View File
@@ -22,6 +22,7 @@
#include "IEffect.hpp"
#include "PiPedalException.hpp"
#include "PiPedalMath.hpp"
#include <assert.h>
namespace pipedal
{
@@ -60,6 +61,18 @@ namespace pipedal
class SplitEffect : public IEffect
{
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;
double sampleRate;
std::vector<float *> inputs;
@@ -283,6 +296,8 @@ namespace pipedal
virtual std::string AtomToJson(uint8_t *pAtom) { return ""; }
virtual std::string GetAtomObjectType(uint8_t*pData) { return "not implemented";}
virtual bool IsVst3() const { return false; }
public:
SplitEffect(
uint64_t instanceId,
@@ -297,7 +312,7 @@ namespace pipedal
}
virtual long GetInstanceId() const
virtual uint64_t GetInstanceId() const
{
return instanceId;
}
+1
View File
@@ -44,6 +44,7 @@ enum class Units {
pc,
s,
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.
":alloc_dealloc_mismatch=0" // Guitarix components trigger this. It's not actually a problem on GCC.
":new_delete_type_mismatch=0" //GxTuner
":detect_odr_violation=0"
":print_stacktrace=1"
":halt_on_error=1"
;
+1 -1
View File
@@ -27,7 +27,7 @@
#include "AvahiService.hpp"
#include "PiPedalSocket.hpp"
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
#include <boost/system/error_code.hpp>
#include <filesystem>
#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 "catch.hpp"
#include <string>
#include "Lv2Host.hpp"
#include "PiPedalHost.hpp"
using namespace piddle;