Initial VST3 Support (disabled)
Disabled until Steinberg licensing compliance can be sorted out.
This commit is contained in:
Vendored
+4
-1
@@ -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",
|
||||
|
||||
Vendored
+2
-3
@@ -90,9 +90,8 @@
|
||||
"span_ext": "cpp",
|
||||
"string_span": "cpp",
|
||||
"util": "cpp",
|
||||
|
||||
|
||||
|
||||
"charconv": "cpp",
|
||||
"valarray": "cpp"
|
||||
},
|
||||
"cSpell.words": [
|
||||
"Guitarix",
|
||||
|
||||
+1
-1
@@ -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.
@@ -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 |
@@ -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,5 +1,5 @@
|
||||
{
|
||||
"socket_server_port": 80,
|
||||
"socket_server_port": 8080,
|
||||
"socket_server_address": "*",
|
||||
"debug": true,
|
||||
"max_upload_size": 1048576,
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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(¶m, 0, sizeof(param));
|
||||
param.sched_priority = 79;
|
||||
param.sched_priority = RT_THREAD_PRIORITY;
|
||||
|
||||
int result = sched_setscheduler(0, SCHED_RR, ¶m);
|
||||
if (result == 0)
|
||||
|
||||
+279
-8
@@ -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
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
#include "pch.h"
|
||||
#include "Lv2EventBufferWriter.hpp"
|
||||
#include "Lv2Host.hpp"
|
||||
#include "PiPedalHost.hpp"
|
||||
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
}};
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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, ¤tPriority);
|
||||
|
||||
struct sched_param param;
|
||||
memset(¶m, 0, sizeof(param));
|
||||
param.sched_priority = RT_THREAD_PRIORITY;
|
||||
|
||||
int result = sched_setscheduler(0, SCHED_RR, ¶m);
|
||||
if (result != 0)
|
||||
{
|
||||
Lv2Log::error(SS("Failed to set ALSA AudioThread priority. (" << strerror(result) << ")"));
|
||||
}
|
||||
}
|
||||
~RtInversionGuard()
|
||||
{
|
||||
sched_setscheduler(0, currentScheduler, ¤tPriority);
|
||||
}
|
||||
};
|
||||
}
|
||||
+1
-1
@@ -20,7 +20,7 @@
|
||||
#include "pch.h"
|
||||
#include "SplitEffect.hpp"
|
||||
|
||||
#include "Lv2Host.hpp"
|
||||
#include "PiPedalHost.hpp"
|
||||
#include "PedalBoard.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
+16
-1
@@ -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;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ enum class Units {
|
||||
pc,
|
||||
s,
|
||||
semitone12TET,
|
||||
custom
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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){};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 = "");
|
||||
|
||||
};
|
||||
|
||||
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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
Submodule submodules/pipedal_p2pd updated: 5b037c80b0...862a02937d
+1
-1
@@ -19,7 +19,7 @@
|
||||
#include "pch.h"
|
||||
#include "catch.hpp"
|
||||
#include <string>
|
||||
#include "Lv2Host.hpp"
|
||||
#include "PiPedalHost.hpp"
|
||||
|
||||
using namespace piddle;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user