code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<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"
   width="100mm"
   height="100mm"
   viewBox="0 0 100 100"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.1 r15371"
   sodipodi:docname="y_linear.svg">
  <defs
     id="defs2">
    <marker
       inkscape:isstock="true"
       style="overflow:visible"
       id="marker8614"
       refX="0"
       refY="0"
       orient="auto"
       inkscape:stockid="TriangleOutM">
      <path
         inkscape:connector-curvature="0"
         transform="scale(0.4)"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         d="M 5.77,0 -2.88,5 V -5 Z"
         id="path8612" />
    </marker>
    <marker
       inkscape:isstock="true"
       style="overflow:visible"
       id="marker8538"
       refX="0"
       refY="0"
       orient="auto"
       inkscape:stockid="TriangleInM">
      <path
         inkscape:connector-curvature="0"
         transform="scale(-0.4)"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         d="M 5.77,0 -2.88,5 V -5 Z"
         id="path8536" />
    </marker>
    <marker
       inkscape:stockid="Arrow2Mstart"
       orient="auto"
       refY="0"
       refX="0"
       id="marker9923"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path9921"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         transform="scale(0.6)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="Arrow2Mend"
       orient="auto"
       refY="0"
       refX="0"
       id="Arrow2Mend"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8461"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         transform="scale(-0.6)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="Arrow2Mstart"
       orient="auto"
       refY="0"
       refX="0"
       id="Arrow2Mstart"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8458"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         transform="scale(0.6)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:isstock="true"
       style="overflow:visible"
       id="marker9470"
       refX="0"
       refY="0"
       orient="auto"
       inkscape:stockid="Arrow2Send">
      <path
         transform="matrix(-0.3,0,0,-0.3,0.69,0)"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         id="path9468"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:isstock="true"
       style="overflow:visible"
       id="marker9424"
       refX="0"
       refY="0"
       orient="auto"
       inkscape:stockid="Arrow2Sstart">
      <path
         transform="matrix(0.3,0,0,0.3,-0.69,0)"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         id="path9422"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="Arrow1Mstart"
       orient="auto"
       refY="0"
       refX="0"
       id="Arrow1Mstart"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8440"
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         transform="matrix(0.4,0,0,0.4,4,0)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="TriangleOutL"
       orient="auto"
       refY="0"
       refX="0"
       id="TriangleOutL"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8576"
         d="M 5.77,0 -2.88,5 V -5 Z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         transform="scale(0.8)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="TriangleInL"
       orient="auto"
       refY="0"
       refX="0"
       id="TriangleInL"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8567"
         d="M 5.77,0 -2.88,5 V -5 Z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         transform="scale(-0.8)"
         inkscape:connector-curvature="0" />
    </marker>
  </defs>
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="1"
     inkscape:cx="-140.02362"
     inkscape:cy="188.97638"
     inkscape:document-units="mm"
     inkscape:current-layer="layer1"
     showgrid="false"
     inkscape:pagecheckerboard="true"
     fit-margin-top="0"
     fit-margin-left="0"
     fit-margin-right="0"
     fit-margin-bottom="0" />
  <metadata
     id="metadata5">
    <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 />
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(-59.999998,-107)">
    <g
       aria-label="LOG"
       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';letter-spacing:0px;word-spacing:0px;fill:#8a8e96;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="text8595"
       transform="translate(-2.39e-4,-3.2244934)">
      <path
         d="m 77.419237,123.94899 h 5.977241 v 20.70502 h 10.47309 v 5.01262 H 77.419237 Z"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#8a8e96;fill-opacity:1;stroke-width:0.26458332px"
         id="path26803"
         inkscape:connector-curvature="0" />
      <path
         d="m 107.01261,128.28981 q -2.72163,0 -4.23747,2.23931 -1.49862,2.23931 -1.49862,6.30452 0,4.04799 1.49862,6.2873 1.51584,2.23932 4.23747,2.23932 2.73885,0 4.23747,-2.23932 1.51584,-2.23931 1.51584,-6.2873 0,-4.06521 -1.51584,-6.30452 -1.49862,-2.23931 -4.23747,-2.23931 z m 0,-4.80591 q 5.58105,0 8.73332,3.54845 3.16948,3.54845 3.16948,9.80129 0,6.23563 -3.16948,9.78408 -3.15227,3.54844 -8.73332,3.54844 -5.56383,0 -8.733319,-3.54844 -3.169488,-3.54845 -3.169488,-9.78408 0,-6.25284 3.169488,-9.80129 3.169489,-3.54845 8.733319,-3.54845 z"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#8a8e96;fill-opacity:1;stroke-width:0.26458332px"
         id="path26805"
         inkscape:connector-curvature="0" />
      <path
         d="m 144.25409,147.7546 q -2.23931,1.20578 -4.65088,1.80867 -2.39434,0.60289 -4.94371,0.60289 -5.78776,0 -9.16395,-3.58289 -3.3762,-3.60013 -3.3762,-9.74963 0,-6.21839 3.42787,-9.78407 3.4451,-3.56567 9.43956,-3.56567 2.30822,0 4.42695,0.48231 2.11874,0.48232 3.99631,1.42972 v 5.32267 q -1.94648,-1.22301 -3.8585,-1.8259 -1.91203,-0.60289 -3.84128,-0.60289 -3.56568,0 -5.49493,2.22208 -1.92926,2.20487 -1.92926,6.32175 0,4.08244 1.86036,6.30453 1.86035,2.22209 5.28822,2.22209 0.93017,0 1.72255,-0.12058 0.80959,-0.13781 1.42971,-0.41341 v -4.99539 h -3.63458 v -4.44418 h 9.30176 z"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#8a8e96;fill-opacity:1;stroke-width:0.26458332px"
         id="path26807"
         inkscape:connector-curvature="0" />
    </g>
    <g
       aria-label="Y"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="text8591"
       transform="translate(0,-1.0583333)">
      <path
         d="m 93.796176,158.56707 h 9.427504 l 7.61366,11.91314 7.61366,-11.91314 h 9.4499 l -12.74169,19.34766 V 192 h -8.62135 v -14.08527 z"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:45.86111069px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold';text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332px"
         id="path35942"
         inkscape:connector-curvature="0" />
    </g>
  </g>
</svg>


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<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"
   width="100mm"
   height="100mm"
   viewBox="0 0 100 100"
   version="1.1"
   id="svg8"
   inkscape:version="0.92.1 r15371"
   sodipodi:docname="y_log.svg">
  <defs
     id="defs2">
    <marker
       inkscape:isstock="true"
       style="overflow:visible"
       id="marker8614"
       refX="0"
       refY="0"
       orient="auto"
       inkscape:stockid="TriangleOutM">
      <path
         inkscape:connector-curvature="0"
         transform="scale(0.4)"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         d="M 5.77,0 -2.88,5 V -5 Z"
         id="path8612" />
    </marker>
    <marker
       inkscape:isstock="true"
       style="overflow:visible"
       id="marker8538"
       refX="0"
       refY="0"
       orient="auto"
       inkscape:stockid="TriangleInM">
      <path
         inkscape:connector-curvature="0"
         transform="scale(-0.4)"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         d="M 5.77,0 -2.88,5 V -5 Z"
         id="path8536" />
    </marker>
    <marker
       inkscape:stockid="Arrow2Mstart"
       orient="auto"
       refY="0"
       refX="0"
       id="marker9923"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path9921"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         transform="scale(0.6)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="Arrow2Mend"
       orient="auto"
       refY="0"
       refX="0"
       id="Arrow2Mend"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8461"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         transform="scale(-0.6)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="Arrow2Mstart"
       orient="auto"
       refY="0"
       refX="0"
       id="Arrow2Mstart"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8458"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         transform="scale(0.6)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:isstock="true"
       style="overflow:visible"
       id="marker9470"
       refX="0"
       refY="0"
       orient="auto"
       inkscape:stockid="Arrow2Send">
      <path
         transform="matrix(-0.3,0,0,-0.3,0.69,0)"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         id="path9468"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:isstock="true"
       style="overflow:visible"
       id="marker9424"
       refX="0"
       refY="0"
       orient="auto"
       inkscape:stockid="Arrow2Sstart">
      <path
         transform="matrix(0.3,0,0,0.3,-0.69,0)"
         d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
         id="path9422"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="Arrow1Mstart"
       orient="auto"
       refY="0"
       refX="0"
       id="Arrow1Mstart"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8440"
         d="M 0,0 5,-5 -12.5,0 5,5 Z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         transform="matrix(0.4,0,0,0.4,4,0)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="TriangleOutL"
       orient="auto"
       refY="0"
       refX="0"
       id="TriangleOutL"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8576"
         d="M 5.77,0 -2.88,5 V -5 Z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         transform="scale(0.8)"
         inkscape:connector-curvature="0" />
    </marker>
    <marker
       inkscape:stockid="TriangleInL"
       orient="auto"
       refY="0"
       refX="0"
       id="TriangleInL"
       style="overflow:visible"
       inkscape:isstock="true">
      <path
         id="path8567"
         d="M 5.77,0 -2.88,5 V -5 Z"
         style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1.00000003pt;stroke-opacity:1"
         transform="scale(-0.8)"
         inkscape:connector-curvature="0" />
    </marker>
  </defs>
  <sodipodi:namedview
     id="base"
     pagecolor="#ffffff"
     bordercolor="#666666"
     borderopacity="1.0"
     inkscape:pageopacity="0.0"
     inkscape:pageshadow="2"
     inkscape:zoom="1"
     inkscape:cx="-140.02362"
     inkscape:cy="188.97638"
     inkscape:document-units="mm"
     inkscape:current-layer="layer1"
     showgrid="false"
     inkscape:pagecheckerboard="true"
     fit-margin-top="0"
     fit-margin-left="0"
     fit-margin-right="0"
     fit-margin-bottom="0" />
  <metadata
     id="metadata5">
    <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 />
      </cc:Work>
    </rdf:RDF>
  </metadata>
  <g
     inkscape:label="Layer 1"
     inkscape:groupmode="layer"
     id="layer1"
     transform="translate(-59.999998,-107)">
    <g
       aria-label="LOG"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="text8595"
       transform="translate(0,-3.2244934)">
      <path
         d="m 77.419237,123.94899 h 5.977241 v 20.70502 h 10.47309 v 5.01262 H 77.419237 Z"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:0.26458332px"
         id="path17641"
         inkscape:connector-curvature="0" />
      <path
         d="m 107.01261,128.28981 q -2.72163,0 -4.23747,2.23931 -1.49862,2.23931 -1.49862,6.30452 0,4.04799 1.49862,6.2873 1.51584,2.23932 4.23747,2.23932 2.73885,0 4.23747,-2.23932 1.51584,-2.23931 1.51584,-6.2873 0,-4.06521 -1.51584,-6.30452 -1.49862,-2.23931 -4.23747,-2.23931 z m 0,-4.80591 q 5.58105,0 8.73332,3.54845 3.16948,3.54845 3.16948,9.80129 0,6.23563 -3.16948,9.78408 -3.15227,3.54844 -8.73332,3.54844 -5.56383,0 -8.733319,-3.54844 -3.169488,-3.54845 -3.169488,-9.78408 0,-6.25284 3.169488,-9.80129 3.169489,-3.54845 8.733319,-3.54845 z"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:0.26458332px"
         id="path17643"
         inkscape:connector-curvature="0" />
      <path
         d="m 144.25409,147.7546 q -2.23931,1.20578 -4.65088,1.80867 -2.39434,0.60289 -4.94371,0.60289 -5.78776,0 -9.16395,-3.58289 -3.3762,-3.60013 -3.3762,-9.74963 0,-6.21839 3.42787,-9.78407 3.4451,-3.56567 9.43956,-3.56567 2.30822,0 4.42695,0.48231 2.11874,0.48232 3.99631,1.42972 v 5.32267 q -1.94648,-1.22301 -3.8585,-1.8259 -1.91203,-0.60289 -3.84128,-0.60289 -3.56568,0 -5.49493,2.22208 -1.92926,2.20487 -1.92926,6.32175 0,4.08244 1.86036,6.30453 1.86035,2.22209 5.28822,2.22209 0.93017,0 1.72255,-0.12058 0.80959,-0.13781 1.42971,-0.41341 v -4.99539 h -3.63458 v -4.44418 h 9.30176 z"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:semi-condensed;font-size:35.27777863px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold Semi-Condensed';text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:0.26458332px"
         id="path17645"
         inkscape:connector-curvature="0" />
    </g>
    <g
       aria-label="Y"
       style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:1.76388884px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
       id="text8591"
       transform="translate(0,-1.0583333)">
      <path
         d="m 93.796176,158.56707 h 9.427504 l 7.61366,11.91314 7.61366,-11.91314 h 9.4499 l -12.74169,19.34766 V 192 h -8.62135 v -14.08527 z"
         style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:45.86111069px;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans Bold';text-align:center;text-anchor:middle;fill:#ffffff;stroke-width:0.26458332px"
         id="path17638"
         inkscape:connector-curvature="0" />
    </g>
  </g>
</svg>

repo_name: CYBERDEViL/LMMS

license: mit


code:
INCLUDE(BuildPlugin)
BUILD_PLUGIN(vectorscope Vectorscope.cpp VecControls.cpp VecControlsDialog.cpp VectorView.cpp
MOCFILES VecControls.h VecControlsDialog.h VectorView.h EMBEDDED_RESOURCES logo.png)


code:
# Vectorscope plugin

## Overview

Vectorscope is a simple stereo field visualizer. Samples are plotted into a graph, with left and right channels providing the coordinates. Previously drawn samples quickly fade away and are continuously replaced by new samples, creating a real-time plot of the most recently played samples.

Similar to other effect plugins, the top-level widget is VecControlDialog. It displays configuration knobs and the main VectorView widget. The back-end configuration class is VecControls, which holds all models and configuration values.

VectorView computes and shows the plot. It gets data for processing from the Vectorscope class, which handles the interface with LMMS. In order to avoid any stalling of the realtime-sensitive audio thread, data are exchanged through a lockless ring buffer.

## Changelog

	1.0.0	2019-11-21
		- initial release


code:
/*
 * VecControls.cpp - definition of VecControls class.
 *
 * Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "VecControls.h"

#include <QtXml/QDomElement>

#include "VecControlsDialog.h"
#include "Vectorscope.h"

VecControls::VecControls(Vectorscope *effect) :
	EffectControls(effect),
	m_effect(effect),

	// initialize models and set default values
	m_persistenceModel(0.5f, 0.0f, 1.0f, 0.05f, this, tr("Display persistence amount")),
	m_logarithmicModel(false, this, tr("Logarithmic scale")),
	m_highQualityModel(false, this, tr("High quality"))
{
	// Colors (percentages include sRGB gamma correction)
	m_colorFG = QColor(60, 255, 130, 255);		// ~LMMS green
	m_colorGrid = QColor(76, 80, 84, 128);		// ~60 % gray (slightly cold / blue), 50 % transparent
	m_colorLabels = QColor(76, 80, 84, 255);	// ~60 % gray (slightly cold / blue)
	m_colorOutline = QColor(30, 34, 38, 255);	// ~40 % gray (slightly cold / blue)
}

// Create the VecControlDialog widget which handles display of GUI elements.
EffectControlDialog* VecControls::createView()
{
	return new VecControlsDialog(this);
}

void VecControls::loadSettings(const QDomElement &element)
{
	m_persistenceModel.loadSettings(element, "Persistence");
	m_logarithmicModel.loadSettings(element, "Logarithmic");
	m_highQualityModel.loadSettings(element, "HighQuality");
}

void VecControls::saveSettings(QDomDocument &document, QDomElement &element)
{
	m_persistenceModel.saveSettings(document, element, "Persistence");
	m_logarithmicModel.saveSettings(document, element, "Logarithmic");
	m_highQualityModel.saveSettings(document, element, "HighQuality");
}


code:
/*
 * VecControls.h - declaration of VecControls class.
 *
 * Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#ifndef VECCONTROLS_H
#define VECCONTROLS_H

#include <QColor>

#include "EffectControls.h"

class Vectorscope;

// Holds all the configuration values
class VecControls : public EffectControls
{
	Q_OBJECT
public:
	explicit VecControls(Vectorscope *effect);
	virtual ~VecControls() {}

	EffectControlDialog *createView() override;

	void saveSettings (QDomDocument &document, QDomElement &element) override;
	void loadSettings (const QDomElement &element) override;

	QString nodeName() const override {return "Vectorscope";}
	int controlCount() override {return 3;}

private:
	Vectorscope *m_effect;

	FloatModel m_persistenceModel;
	BoolModel m_logarithmicModel;
	BoolModel m_highQualityModel;

	QColor m_colorFG;
	QColor m_colorGrid;
	QColor m_colorLabels;
	QColor m_colorOutline;

	friend class VecControlsDialog;
	friend class VectorView;
};
#endif // VECCONTROLS_H


code:
/*
 * VecControlsDialog.cpp - definition of VecControlsDialog class.
 *
 * Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "VecControlsDialog.h"

#include <QGridLayout>
#include <QLabel>
#include <QResizeEvent>
#include <QSizePolicy>
#include <QWidget>

#include "embed.h"
#include "LedCheckbox.h"
#include "VecControls.h"
#include "Vectorscope.h"
#include "VectorView.h"

// The entire GUI layout is built here.
VecControlsDialog::VecControlsDialog(VecControls *controls) :
	EffectControlDialog(controls),
	m_controls(controls)
{
	QVBoxLayout *master_layout = new QVBoxLayout;
	master_layout->setContentsMargins(0, 2, 0, 0);
	setLayout(master_layout);

	// Visualizer widget
	// The size of 768 pixels seems to offer a good balance of speed, accuracy and trace thickness.
	VectorView *display = new VectorView(controls, m_controls->m_effect->getBuffer(), 768, this);
	master_layout->addWidget(display);

	// Config area located inside visualizer
	QVBoxLayout *internal_layout = new QVBoxLayout(display);
	QHBoxLayout *config_layout = new QHBoxLayout();
	QVBoxLayout *switch_layout = new QVBoxLayout();
	internal_layout->addStretch();
	internal_layout->addLayout(config_layout);
	config_layout->addLayout(switch_layout);

	// High-quality switch
	LedCheckBox *highQualityButton = new LedCheckBox(tr("HQ"), this);
	highQualityButton->setToolTip(tr("Double the resolution and simulate continuous analog-like trace."));
	highQualityButton->setCheckable(true);
	highQualityButton->setMinimumSize(70, 12);
	highQualityButton->setModel(&controls->m_highQualityModel);
	switch_layout->addWidget(highQualityButton);

	// Log. scale switch
	LedCheckBox *logarithmicButton = new LedCheckBox(tr("Log. scale"), this);
	logarithmicButton->setToolTip(tr("Display amplitude on logarithmic scale to better see small values."));
	logarithmicButton->setCheckable(true);
	logarithmicButton->setMinimumSize(70, 12);
	logarithmicButton->setModel(&controls->m_logarithmicModel);
	switch_layout->addWidget(logarithmicButton);

	config_layout->addStretch();

	// Persistence knob
	Knob *persistenceKnob = new Knob(knobSmall_17, this);
	persistenceKnob->setModel(&controls->m_persistenceModel);
	persistenceKnob->setLabel(tr("Persist."));
	persistenceKnob->setToolTip(tr("Trace persistence: higher amount means the trace will stay bright for longer time."));
	persistenceKnob->setHintText(tr("Trace persistence"), "");
	config_layout->addWidget(persistenceKnob);
}

// Suggest the best widget size.
QSize VecControlsDialog::sizeHint() const
{
	return QSize(275, 300);
}


code:
/*
 * VecControlsDialog.h - declatation of VecControlsDialog class.
 *
 * Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#ifndef VECCONTROLSDIALOG_H
#define VECCONTROLSDIALOG_H

#include "EffectControlDialog.h"

class VecControls;

//! Top-level widget holding the configuration GUI and vector display
class VecControlsDialog : public EffectControlDialog
{
	Q_OBJECT
public:
	explicit VecControlsDialog(VecControls *controls);
	virtual ~VecControlsDialog() {}

	bool isResizable() const override {return true;}
	QSize sizeHint() const override;

private:
	VecControls *m_controls;
};

#endif // VECCONTROLSDIALOG_H


code:
/* VectorView.cpp - implementation of VectorView class.
 *
 * Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
 *
 * This file is part of LMMS - https://lmms.io
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "VectorView.h"

#include <algorithm>
#include <chrono>
#include <cmath>
#include <QImage>
#include <QPainter>

#include "ColorChooser.h"
#include "GuiApplication.h"
#include "MainWindow.h"

VectorView::VectorView(VecControls *controls, LocklessRingBuffer<sampleFrame> *inputBuffer, unsigned short displaySize, QWidget *parent) :
	QWidget(parent),
	m_controls(controls),
	m_inputBuffer(inputBuffer),
	m_bufferReader(*inputBuffer),
	m_displaySize(displaySize),
	m_zoom(1.f),
	m_persistTimestamp(0),
	m_zoomTimestamp(0),
	m_oldHQ(m_controls->m_highQualityModel.value()),
	m_oldX(m_displaySize / 2),
	m_oldY(m_displaySize / 2)
{
	setMinimumSize(200, 200);
	setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

	connect(gui->mainWindow(), SIGNAL(periodicUpdate()), this, SLOT(periodicUpdate()));

	m_displayBuffer.resize(sizeof qRgb(0,0,0) * m_displaySize * m_displaySize, 0);

#ifdef VEC_DEBUG
	m_executionAvg = 0;
#endif
}

// Compose and draw all the content; called by Qt.
void VectorView::paintEvent(QPaintEvent *event)
{
#ifdef VEC_DEBUG
	unsigned int drawTime = std::chrono::high_resolution_clock::now().time_since_epoch().count();
#endif

	// All drawing done in this method, local variables are sufficient for the boundary
	const int displayTop = 2;
	const int displayBottom = height() - 2;
	const int displayLeft = 2;
	const int displayRight = width() - 2;
	const int displayWidth = displayRight - displayLeft;
	const int displayHeight = displayBottom - displayTop;

	const float centerX = displayLeft + (displayWidth / 2.f);
	const float centerY = displayTop + (displayWidth / 2.f);

	const int margin = 4;
	const int gridCorner = 30;

	// Setup QPainter and font sizes
	QPainter painter(this);
	painter.setRenderHint(QPainter::Antialiasing, true);

	QFont normalFont, boldFont;
	boldFont.setPixelSize(26);
	boldFont.setBold(true);
	const int labelWidth = 26;
	const int labelHeight = 26;

	bool hq = m_controls->m_highQualityModel.value();

	// Clear display buffer if quality setting was changed
	if (hq != m_oldHQ)
	{
		m_oldHQ = hq;
		for (std::size_t i = 0; i < m_displayBuffer.size(); i++)
		{
			m_displayBuffer.data()[i] = 0;
		}
	}

	// Dim stored image based on persistence setting and elapsed time.
	// Update period is limited to 50 ms (20 FPS) for non-HQ mode and 10 ms (100 FPS) for HQ mode.
	const unsigned int currentTimestamp = std::chrono::duration_cast<std::chrono::milliseconds>
	(
		std::chrono::high_resolution_clock::now().time_since_epoch()
	).count();
	const unsigned int elapsed = currentTimestamp - m_persistTimestamp;
	const unsigned int threshold = hq ? 10 : 50;
	if (elapsed > threshold)
	{
		m_persistTimestamp = currentTimestamp;
		// Non-HQ mode uses half the resolution → use limited buffer space.
		const std::size_t useableBuffer = hq ? m_displayBuffer.size() : m_displayBuffer.size() / 4;
		// The knob value is interpreted on log. scale, otherwise the effect would ramp up too slowly.
		// Persistence value specifies fraction of light intensity that remains after 10 ms.
		// → Compensate it based on elapsed time (exponential decay).
		const float persist = log10(1 + 9 * m_controls->m_persistenceModel.value());
		const float persistPerFrame = pow(persist, elapsed / 10.f);
		// Note that for simplicity and performance reasons, this implementation only dims all stored
		// values by a given factor. A true simulation would also do the inverse of desaturation that
		// occurs in high-intensity traces in HQ mode.
		for (std::size_t i = 0; i < useableBuffer; i++)
		{
			m_displayBuffer.data()[i] *= persistPerFrame;
		}
	}

	// Get new samples from the lockless input FIFO buffer
	auto inBuffer = m_bufferReader.read_max(m_inputBuffer->capacity());
	std::size_t frameCount = inBuffer.size();

	// Draw new points on top
	float left, right;
	int x, y;

	const bool logScale = m_controls->m_logarithmicModel.value();
	const unsigned short activeSize = hq ? m_displaySize : m_displaySize / 2;

	// Helper lambda functions for better readability
	// Make sure pixel stays within display bounds:
	auto saturate = [=](short pixelPos) {return qBound((short)0, pixelPos, (short)(activeSize - 1));};
	// Take existing pixel and brigthen it. Very bright light should reduce saturation and become
	// white. This effect is easily approximated by capping elementary colors to 255 individually.
	auto updatePixel = [&](unsigned short x, unsigned short y, QColor addedColor)
	{
		QColor currentColor = ((QRgb*)m_displayBuffer.data())[x + y * activeSize];
		currentColor.setRed(std::min(currentColor.red() + addedColor.red(), 255));
		currentColor.setGreen(std::min(currentColor.green() + addedColor.green(), 255));
		currentColor.setBlue(std::min(currentColor.blue() + addedColor.blue(), 255));
		((QRgb*)m_displayBuffer.data())[x + y * activeSize] = currentColor.rgb();
	};

	if (hq)
	{
		// High quality mode: check distance between points and draw a line.
		// The longer the line is, the dimmer, simulating real electron trace on luminescent screen.
		for (std::size_t frame = 0; frame < frameCount; frame++)
		{
			float inLeft = inBuffer[frame][0] * m_zoom;
			float inRight = inBuffer[frame][1] * m_zoom;
			// Scale left and right channel from (-1.0, 1.0) to display range
			if (logScale)
			{
				// To better preserve shapes, the log scale is applied to the distance from origin,
				// not the individual channels.
				const float distance = sqrt(inLeft * inLeft + inRight * inRight);
				const float distanceLog = log10(1 + 9 * std::abs(distance));
				const float angleCos = inLeft / distance;
				const float angleSin = inRight / distance;
				left  = distanceLog * angleCos * (activeSize - 1) / 4;
				right = distanceLog * angleSin * (activeSize - 1) / 4;
			}
			else
			{
				left  = inLeft * (activeSize - 1) / 4;
				right = inRight * (activeSize - 1) / 4;
			}

			// Rotate display coordinates 45 degrees, flip Y axis and make sure the result stays within bounds
			x = saturate(right - left + activeSize / 2.f);
			y = saturate(activeSize - (right + left + activeSize / 2.f));

			// Estimate number of points needed to fill space between the old and new pixel. Cap at 100.
			unsigned char points = std::min((int)sqrt((m_oldX - x) * (m_oldX - x) + (m_oldY - y) * (m_oldY - y)), 100);

			// Large distance = dim trace. The curve for darker() is choosen so that:
			// - no movement (0 points) actually _increases_ brightness slightly,
			// - one point between samples = returns exactly the specified color,
			// - one to 99 points between samples = follows a sharp "1/x" decaying curve,
			// - 100 points between samples = returns approximately 5 % brightness.
			// Everything else is discarded (by the 100 point cap) because there is not much to see anyway.
			QColor addedColor = m_controls->m_colorFG.darker(75 + 20 * points).rgb();

			// Draw the new pixel: the beam sweeps across area that may have been excited before
			// → add new value to existing pixel state.
			updatePixel(x, y, addedColor);

			// Draw interpolated points between the old pixel and the new one
			int newX = right - left + activeSize / 2.f;
			int newY = activeSize - (right + left + activeSize / 2.f);
			for (unsigned char i = 1; i < points; i++)
			{
				x = saturate(((points - i) * m_oldX + i * newX) / points);
				y = saturate(((points - i) * m_oldY + i * newY) / points);
				updatePixel(x, y, addedColor);
			}
			m_oldX = newX;
			m_oldY = newY;
		}
	}
	else
	{
		// To improve performance, non-HQ mode uses smaller display size and only
		// one full-color pixel per sample.
		for (std::size_t frame = 0; frame < frameCount; frame++)
		{
			float inLeft = inBuffer[frame][0] * m_zoom;
			float inRight = inBuffer[frame][1] * m_zoom;
			if (logScale) {
				const float distance = sqrt(inLeft * inLeft + inRight * inRight);
				const float distanceLog = log10(1 + 9 * std::abs(distance));
				const float angleCos = inLeft / distance;
				const float angleSin = inRight / distance;
				left  = distanceLog * angleCos * (activeSize - 1) / 4;
				right = distanceLog * angleSin * (activeSize - 1) / 4;
			} else {
				left  = inLeft * (activeSize - 1) / 4;
				right = inRight * (activeSize - 1) / 4;
			}
			x = saturate(right - left + activeSize / 2.f);
			y = saturate(activeSize - (right + left + activeSize / 2.f));
			((QRgb*)m_displayBuffer.data())[x + y * activeSize] = m_controls->m_colorFG.rgb();
		}
	}

	// Draw background
	painter.fillRect(displayLeft, displayTop, displayWidth, displayHeight, QColor(0,0,0));

	// Draw the final image
	QImage temp = QImage(m_displayBuffer.data(),
						 activeSize,
						 activeSize,
						 QImage::Format_RGB32);
	temp.setDevicePixelRatio(devicePixelRatio());
	painter.drawImage(displayLeft, displayTop,
					  temp.scaledToWidth(displayWidth * devicePixelRatio(),
					  Qt::SmoothTransformation));

	// Draw the grid and labels
	painter.setPen(QPen(m_controls->m_colorGrid, 1.5, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
	painter.drawEllipse(QPointF(centerX, centerY), displayWidth / 2.f, displayWidth / 2.f);
	painter.setPen(QPen(m_controls->m_colorGrid, 1.5, Qt::DotLine, Qt::RoundCap, Qt::BevelJoin));
	painter.drawLine(QPointF(centerX, centerY), QPointF(displayLeft + gridCorner, displayTop + gridCorner));
	painter.drawLine(QPointF(centerX, centerY), QPointF(displayRight - gridCorner, displayTop + gridCorner));

	painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
	painter.setFont(boldFont);
	painter.drawText(displayLeft + margin, displayTop,
					 labelWidth, labelHeight, Qt::AlignLeft | Qt::AlignTop | Qt::TextDontClip,
					 QString("L"));
	painter.drawText(displayRight - margin - labelWidth, displayTop,
					 labelWidth, labelHeight, Qt::AlignRight| Qt::AlignTop | Qt::TextDontClip,
					 QString("R"));

	// Draw the outline
	painter.setPen(QPen(m_controls->m_colorOutline, 2, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
	painter.drawRoundedRect(1, 1, width() - 2, height() - 2, 2.f, 2.f);

	// Draw zoom info if changed within last second (re-using timestamp acquired for dimming)
	if (currentTimestamp - m_zoomTimestamp < 1000)
	{
		painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
		painter.setFont(normalFont);
		painter.drawText(displayWidth / 2 - 50, displayBottom - 20, 100, 16, Qt::AlignCenter,
						 QString("Zoom: ").append(std::to_string((int)round(m_zoom * 100)).c_str()).append(" %"));
	}

	// Optionally measure drawing performance
#ifdef VEC_DEBUG
	drawTime = std::chrono::high_resolution_clock::now().time_since_epoch().count() - drawTime;
	m_executionAvg = 0.95f * m_executionAvg + 0.05f * drawTime / 1000000.f;
	painter.setPen(QPen(m_controls->m_colorLabels, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin));
	painter.setFont(normalFont);
	painter.drawText(displayWidth / 2 - 50, displayBottom - 16, 100, 16, Qt::AlignLeft,
					 QString("Exec avg.: ").append(std::to_string(m_executionAvg).substr(0, 5).c_str()).append(" ms"));
#endif
}

// Periodically trigger repaint and check if the widget is visible
void VectorView::periodicUpdate()
{
	m_visible = isVisible();
	if (m_visible) {update();}
}

// Allow to change color on double-click.
// More of an Easter egg, to avoid cluttering the interface with non-essential functionality.
void VectorView::mouseDoubleClickEvent(QMouseEvent *event)
{
	ColorChooser *colorDialog = new ColorChooser(m_controls->m_colorFG, this);
	if (colorDialog->exec())
	{
		m_controls->m_colorFG = colorDialog->currentColor();
	}
}

// Change zoom level using the mouse wheel
void VectorView::wheelEvent(QWheelEvent *event)
{
	// Go through integers to avoid accumulating errors
	const unsigned short old_zoom = round(100 * m_zoom);
	// Min-max bounds are 20 and 1000 %, step for 15°-increment mouse wheel is 20 %
	const unsigned short new_zoom = qBound(20, old_zoom + event->angleDelta().y() / 6, 1000);
	m_zoom = new_zoom / 100.f;
	event->accept();
	m_zoomTimestamp = std::chrono::duration_cast<std::chrono::milliseconds>
	(
		std::chrono::high_resolution_clock::now().time_since_epoch()
	).count();

}


code:
/* VectorView.h - declaration of VectorView class.
 *
 * Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */
#ifndef VECTORVIEW_H
#define VECTORVIEW_H

#include <QMouseEvent>
#include <QWheelEvent>
#include <QWidget>

#include "Knob.h"
#include "LedCheckbox.h"
#include "LocklessRingBuffer.h"
#include "VecControls.h"

//#define VEC_DEBUG

// Widget that displays a vectorscope visualization of stereo signal.
class VectorView : public QWidget
{
	Q_OBJECT
public:
	explicit VectorView(VecControls *controls, LocklessRingBuffer<sampleFrame> *inputBuffer, unsigned short displaySize, QWidget *parent = 0);
	virtual ~VectorView() {}

	QSize sizeHint() const override {return QSize(300, 300);}

protected:
	void paintEvent(QPaintEvent *event) override;
	void mouseDoubleClickEvent(QMouseEvent *event) override;
	void wheelEvent(QWheelEvent *event) override;

private slots:
	void periodicUpdate();

private:
	VecControls *m_controls;

	LocklessRingBuffer<sampleFrame> *m_inputBuffer;
	LocklessRingBufferReader<sampleFrame> m_bufferReader;

	std::vector<uchar> m_displayBuffer;
	const unsigned short m_displaySize;

	bool m_visible;

	float m_zoom;

	// State variables for comparison with previous repaint
	unsigned int m_persistTimestamp;
	unsigned int m_zoomTimestamp;
	bool m_oldHQ;
	int m_oldX;
	int m_oldY;

#ifdef VEC_DEBUG
	float m_executionAvg = 0;
#endif
};
#endif // VECTORVIEW_H


code:
/*
 * Vectorscope.cpp - definition of Vectorscope class.
 *
 * Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "Vectorscope.h"

#include "embed.h"
#include "plugin_export.h"

extern "C" {
	Plugin::Descriptor PLUGIN_EXPORT vectorscope_plugin_descriptor =
	{
		STRINGIFY(PLUGIN_NAME),
		"Vectorscope",
		QT_TRANSLATE_NOOP("PluginBrowser", "A stereo field visualizer."),
		"Martin Pavelek <he29/dot/HS/at/gmail/dot/com>",
		0x0100,
		Plugin::Effect,
		new PluginPixmapLoader("logo"),
		NULL,
		NULL
	};
}

Vectorscope::Vectorscope(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) :
	Effect(&vectorscope_plugin_descriptor, parent, key),
	m_controls(this),
	// Buffer is sized to cover 4* the current maximum LMMS audio buffer size,
	// so that it has some reserve space in case GUI thresd is busy.
	m_inputBuffer(4 * m_maxBufferSize)
{
}

// Take audio data and store them for processing and display in the GUI thread.
bool Vectorscope::processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count)
{
	if (!isEnabled() || !isRunning ()) {return false;}

	// Skip processing if the controls dialog isn't visible, it would only waste CPU cycles.
	if (m_controls.isViewVisible())
	{
		// To avoid processing spikes on audio thread, data are stored in
		// a lockless ringbuffer and processed in a separate thread.
		m_inputBuffer.write(buffer, frame_count);
	}
	return isRunning();
}

extern "C" {
	// needed for getting plugin out of shared lib
	PLUGIN_EXPORT Plugin *lmms_plugin_main(Model *parent, void *data)
	{
		return new Vectorscope(parent, static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(data));
	}
}


code:
/* Vectorscope.h - declaration of Vectorscope class.
 *
 * Copyright (c) 2019 Martin Pavelek <he29/dot/HS/at/gmail/dot/com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#ifndef VECTORSCOPE_H
#define VECTORSCOPE_H

#include "Effect.h"
#include "LocklessRingBuffer.h"
#include "VecControls.h"

//! Top level class; handles LMMS interface and accumulates data for processing.
class Vectorscope : public Effect
{
public:
	Vectorscope(Model *parent, const Descriptor::SubPluginFeatures::Key *key);
	virtual ~Vectorscope() {};

	bool processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) override;
	EffectControls *controls() override {return &m_controls;}
	LocklessRingBuffer<sampleFrame> *getBuffer() {return &m_inputBuffer;}

private:
	VecControls m_controls;

	// Maximum LMMS buffer size (hard coded, the actual constant is hard to get)
	const unsigned int m_maxBufferSize = 4096;
	LocklessRingBuffer<sampleFrame> m_inputBuffer;
};

#endif // VECTORSCOPE_H


code:
IF(LMMS_SUPPORT_VST)
INCLUDE(BuildPlugin)
INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../vst_base")
LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/..")
IF(LMMS_BUILD_LINUX)
	LINK_LIBRARIES(-Wl,--enable-new-dtags)
	SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
	SET(CMAKE_INSTALL_RPATH "$ORIGIN")
ELSE()
	SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${PLUGIN_DIR}")
ENDIF()

BUILD_PLUGIN(vsteffect VstEffect.cpp VstEffectControls.cpp VstEffectControlDialog.cpp VstSubPluginFeatures.cpp VstEffect.h VstEffectControls.h VstEffectControlDialog.h VstSubPluginFeatures.h MOCFILES VstEffectControlDialog.h VstEffectControls.h EMBEDDED_RESOURCES *.png)
TARGET_LINK_LIBRARIES(vsteffect vstbase)

ENDIF(LMMS_SUPPORT_VST)


code:
/*
 * VstEffect.cpp - class for handling VST effect plugins
 *
 * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QMessageBox>

#include "VstEffect.h"

#include "GuiApplication.h"
#include "Song.h"
#include "TextFloat.h"
#include "VstSubPluginFeatures.h"

#include "embed.h"
#include "plugin_export.h"

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT vsteffect_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"VST",
	QT_TRANSLATE_NOOP( "PluginBrowser",
				"plugin for using arbitrary VST effects inside LMMS." ),
	"Tobias Doerffel <tobydox/at/users.sf.net>",
	0x0200,
	Plugin::Effect,
	new PluginPixmapLoader("logo"),
	NULL,
	new VstSubPluginFeatures( Plugin::Effect )
} ;

}

VstEffect::VstEffect( Model * _parent,
			const Descriptor::SubPluginFeatures::Key * _key ) :
	Effect( &vsteffect_plugin_descriptor, _parent, _key ),
	m_pluginMutex(),
	m_key( *_key ),
	m_vstControls( this )
{
	if( !m_key.attributes["file"].isEmpty() )
	{
		openPlugin( m_key.attributes["file"] );
	}
	setDisplayName( m_key.attributes["file"].section( ".dll", 0, 0 ).isEmpty()
		? m_key.name : m_key.attributes["file"].section( ".dll", 0, 0 ) );
}

VstEffect::~VstEffect()
{
}

bool VstEffect::processAudioBuffer( sampleFrame * _buf, const fpp_t _frames )
{
	if( !isEnabled() || !isRunning () )
	{
		return false;
	}

	if( m_plugin )
	{
		const float d = dryLevel();
#ifdef __GNUC__
		sampleFrame buf[_frames];
#else
		sampleFrame * buf = new sampleFrame[_frames];
#endif
		memcpy( buf, _buf, sizeof( sampleFrame ) * _frames );
		if (m_pluginMutex.tryLock(Engine::getSong()->isExporting() ? -1 : 0))
		{
			m_plugin->process( buf, buf );
			m_pluginMutex.unlock();
		}

		double out_sum = 0.0;
		const float w = wetLevel();
		for( fpp_t f = 0; f < _frames; ++f )
		{
			_buf[f][0] = w*buf[f][0] + d*_buf[f][0];
			_buf[f][1] = w*buf[f][1] + d*_buf[f][1];
		}
		for( fpp_t f = 0; f < _frames; ++f )
		{
			out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1];
		}
#ifndef __GNUC__
		delete[] buf;
#endif

		checkGate( out_sum / _frames );
	}
	return isRunning();
}

void VstEffect::openPlugin( const QString & _plugin )
{
	TextFloat * tf = NULL;
	if( gui )
	{
		tf = TextFloat::displayMessage(
			VstPlugin::tr( "Loading plugin" ),
			VstPlugin::tr( "Please wait while loading VST plugin..." ),
				PLUGIN_NAME::getIconPixmap( "logo", 24, 24 ), 0 );
	}

	QMutexLocker ml( &m_pluginMutex ); Q_UNUSED( ml );
	m_plugin = QSharedPointer<VstPlugin>(new VstPlugin( _plugin ));
	if( m_plugin->failed() )
	{
		m_plugin.clear();
		delete tf;
		collectErrorForUI( VstPlugin::tr( "The VST plugin %1 could not be loaded." ).arg( _plugin ) );
		return;
	}

	delete tf;

	m_key.attributes["file"] = _plugin;
}

extern "C"
{

// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * _parent, void * _data )
{
	return new VstEffect( _parent,
		static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(
								_data ) );
}

}


code:
/*
 * VstEffect.h - class for handling VST effect plugins
 *
 * Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#ifndef _VST_EFFECT_H
#define _VST_EFFECT_H

#include <QtCore/QMutex>
#include <QtCore/QSharedPointer>

#include "Effect.h"
#include "VstEffectControlDialog.h"
#include "VstEffectControls.h"

class VstPlugin;

class VstEffect : public Effect
{
public:
	VstEffect( Model * _parent,
			const Descriptor::SubPluginFeatures::Key * _key );
	virtual ~VstEffect();

	virtual bool processAudioBuffer( sampleFrame * _buf,
							const fpp_t _frames );

	virtual EffectControls * controls()
	{
		return &m_vstControls;
	}

	virtual inline QString publicName() const
	{
		return m_plugin->name();
	}

private:
	void openPlugin( const QString & _plugin );
	void closePlugin();

	QSharedPointer<VstPlugin> m_plugin;
	QMutex m_pluginMutex;
	EffectKey m_key;

	VstEffectControls m_vstControls;

	friend class VstEffectControls;
	friend class VstEffectControlDialog;
	friend class manageVSTEffectView;

} ;

#endif


code:
/*
 * VstEffectControlDialog.cpp - dialog for displaying VST-effect GUI
 *
 * Copyright (c) 2006-2011 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QLayout>
#include <QMdiArea>
#include <QMenu>
#include <QPushButton>

#include "VstEffectControlDialog.h"
#include "VstEffect.h"

#include "ConfigManager.h"
#include "PixmapButton.h"
#include "embed.h"
#include "ToolTip.h"

#include <QObject>
#include <QPainter>
#include "gui_templates.h"
#include <QToolBar>
#include <QLabel>

VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) :
	EffectControlDialog( _ctl ),
	m_pluginWidget( NULL ),

	m_plugin( NULL ),
	tbLabel( NULL )
{
	QGridLayout * l = new QGridLayout( this );
	l->setContentsMargins( 10, 10, 10, 10 );
	l->setVerticalSpacing( 2 );
	l->setHorizontalSpacing( 2 );

	bool embed_vst = false;

	if( _ctl != NULL && _ctl->m_effect != NULL &&
					_ctl->m_effect->m_plugin != NULL )
	{
		m_plugin = _ctl->m_effect->m_plugin;
		embed_vst = m_plugin->embedMethod() != "none";

		if (embed_vst) {
			if (m_plugin->hasEditor() && ! m_plugin->pluginWidget()) {
				m_plugin->createUI(this);
			}
			m_pluginWidget = m_plugin->pluginWidget();
		}
	}

	if (m_plugin)
	{
		setWindowTitle( m_plugin->name() );

		QPushButton * btn = new QPushButton( tr( "Show/hide" ));

		if (embed_vst) {
			btn->setCheckable( true );
			btn->setChecked( true );
			connect( btn, SIGNAL( toggled( bool ) ),
						SLOT( togglePluginUI( bool ) ) );
		} else {
			connect( btn, SIGNAL( clicked() ),
						m_plugin.data(), SLOT( toggleUI() ) );
		}

		btn->setMinimumWidth( 78 );
		btn->setMaximumWidth( 78 );
		btn->setMinimumHeight( 24 );
		btn->setMaximumHeight( 24 );
		m_togglePluginButton = btn;

		m_managePluginButton = new PixmapButton( this, "" );
		m_managePluginButton->setCheckable( false );
		m_managePluginButton->setCursor( Qt::PointingHandCursor );
		m_managePluginButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
							"controls_active" ) );
		m_managePluginButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"controls" ) );
		connect( m_managePluginButton, SIGNAL( clicked() ),  _ctl,
						SLOT( managePlugin() ) );
		ToolTip::add( m_managePluginButton, tr( "Control VST plugin from LMMS host" ) );

		m_managePluginButton->setMinimumWidth( 26 );
		m_managePluginButton->setMaximumWidth( 26 );
		m_managePluginButton->setMinimumHeight( 21 );
		m_managePluginButton->setMaximumHeight( 21 );

		m_openPresetButton = new PixmapButton( this, "" );
		m_openPresetButton->setCheckable( false );
		m_openPresetButton->setCursor( Qt::PointingHandCursor );
		m_openPresetButton->setActiveGraphic( embed::getIconPixmap(
							"project_open" ) );
		m_openPresetButton->setInactiveGraphic( embed::getIconPixmap(
							"project_open" ) );
		connect( m_openPresetButton, SIGNAL( clicked() ), _ctl,
						SLOT( openPreset() ) );
		ToolTip::add( m_openPresetButton, tr( "Open VST plugin preset" ) );

		m_openPresetButton->setMinimumWidth( 16 );
		m_openPresetButton->setMaximumWidth( 16 );
		m_openPresetButton->setMinimumHeight( 16 );
		m_openPresetButton->setMaximumHeight( 16 );

		m_rolLPresetButton = new PixmapButton( this, "" );
		m_rolLPresetButton->setCheckable( false );
		m_rolLPresetButton->setCursor( Qt::PointingHandCursor );
		m_rolLPresetButton->setActiveGraphic( embed::getIconPixmap(
							"stepper-left-press" ) );
		m_rolLPresetButton->setInactiveGraphic( embed::getIconPixmap(
							"stepper-left" ) );
		connect( m_rolLPresetButton, SIGNAL( clicked() ), _ctl,
						SLOT( rolrPreset() ) );

		connect( m_rolLPresetButton, SIGNAL( clicked() ), this,
						SLOT( update() ) );

		ToolTip::add( m_rolLPresetButton, tr( "Previous (-)" ) );

		m_rolLPresetButton->setShortcut( Qt::Key_Minus );

		m_rolLPresetButton->setMinimumWidth( 16 );
		m_rolLPresetButton->setMaximumWidth( 16 );
		m_rolLPresetButton->setMinimumHeight( 16 );
		m_rolLPresetButton->setMaximumHeight( 16 );

		m_rolRPresetButton = new PixmapButton( this, "" );
		m_rolRPresetButton->setCheckable( false );
		m_rolRPresetButton->setCursor( Qt::PointingHandCursor );
		m_rolRPresetButton->setActiveGraphic( embed::getIconPixmap(
							"stepper-right-press" ) );
		m_rolRPresetButton->setInactiveGraphic( embed::getIconPixmap(
							"stepper-right" ) );
		connect( m_rolRPresetButton, SIGNAL( clicked() ), _ctl,
						SLOT( rollPreset() ) );

		connect( m_rolRPresetButton, SIGNAL( clicked() ), this,
						SLOT( update() ) );

		ToolTip::add( m_rolRPresetButton, tr( "Next (+)" ) );

		m_rolRPresetButton->setShortcut( Qt::Key_Plus );

		m_rolRPresetButton->setMinimumWidth( 16 );
		m_rolRPresetButton->setMaximumWidth( 16 );
		m_rolRPresetButton->setMinimumHeight( 16 );
		m_rolRPresetButton->setMaximumHeight( 16 );

 		_ctl->m_selPresetButton = new QPushButton( tr( "" ), this );

		_ctl->m_selPresetButton->setCheckable( false );
		_ctl->m_selPresetButton->setCursor( Qt::PointingHandCursor );
		_ctl->m_selPresetButton->setIcon( embed::getIconPixmap( "stepper-down" ) );

		QMenu * menu = new QMenu;
		connect( menu, SIGNAL( aboutToShow() ), _ctl, SLOT( updateMenu() ) );

 		_ctl->m_selPresetButton->setMenu(menu);

		_ctl->m_selPresetButton->setMinimumWidth( 16 );
		_ctl->m_selPresetButton->setMaximumWidth( 16 );
		_ctl->m_selPresetButton->setMinimumHeight( 16 );
		_ctl->m_selPresetButton->setMaximumHeight( 16 );

		m_savePresetButton = new PixmapButton( this, "" );
		m_savePresetButton->setCheckable( false );
		m_savePresetButton->setCursor( Qt::PointingHandCursor );
		m_savePresetButton->setActiveGraphic( embed::getIconPixmap(
							"project_save", 21, 21  ) );
		m_savePresetButton->setInactiveGraphic( embed::getIconPixmap(
							"project_save", 21, 21  ) );
		connect( m_savePresetButton, SIGNAL( clicked() ), _ctl,
						SLOT( savePreset() ) );
		ToolTip::add( m_savePresetButton, tr( "Save preset" ) );

		m_savePresetButton->setMinimumWidth( 21 );
		m_savePresetButton->setMaximumWidth( 21 );
		m_savePresetButton->setMinimumHeight( 21 );
		m_savePresetButton->setMaximumHeight( 21 );

		int newSize = 0;

		if (m_pluginWidget) {
			newSize = m_pluginWidget->width() + 20;
		}
		newSize = std::max(newSize, 250);

		QWidget* resize = new QWidget(this);
		resize->resize( newSize, 10 );
		QWidget* space0 = new QWidget(this);
		space0->resize(8, 10);
		QWidget* space1 = new QWidget(this);
		space1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
		QFont f( "Arial", 10 );

		l->addItem( new QSpacerItem( newSize - 20, 30, QSizePolicy::Fixed,
						QSizePolicy::Fixed ), 1, 0 );
		l->addWidget( resize, 2, 0, 1, 1, Qt::AlignCenter );
		if (m_pluginWidget) {
			l->addWidget( m_pluginWidget, 3, 0, 1, 1, Qt::AlignCenter );
		}
		l->setRowStretch( 5, 1 );
		l->setColumnStretch( 1, 1 );

		QToolBar * tb = new QToolBar( this );
		tb->resize( newSize , 32 );
		tb->addWidget(space0);
		tb->addWidget( m_rolLPresetButton );
		tb->addWidget( m_rolRPresetButton );
		tb->addWidget( _ctl->m_selPresetButton );
		tb->addWidget( m_openPresetButton );
		tb->addWidget( m_savePresetButton );
		tb->addWidget( m_managePluginButton );
		tb->addWidget( btn );
		tb->addWidget(space1);

		tbLabel = new QLabel( tr( "Effect by: " ), this );
		tbLabel->setFont( pointSize<7>( f ) );
		tbLabel->setTextFormat(Qt::RichText);
		tbLabel->setAlignment( Qt::AlignTop | Qt::AlignLeft );
		tb->addWidget( tbLabel );
	}
}

void VstEffectControlDialog::paintEvent( QPaintEvent * )
{
	if( m_plugin != NULL && tbLabel != NULL )
	{
		tbLabel->setText( tr( "Effect by: " ) + m_plugin->vendorString() +
			tr( "       <br />" ) +
			m_plugin->currentProgramName() );
	}
}

void VstEffectControlDialog::showEvent(QShowEvent *_se)
{
	EffectControlDialog::showEvent( _se );
	// Workaround for a (unexplained) bug where on project-load the effect
	// control window has size 0 and would only restore to the proper size upon
	// moving the window or interacting with it.
	if (parentWidget()) {
		parentWidget()->adjustSize();
	}
}

VstEffectControlDialog::~VstEffectControlDialog()
{
	if (m_pluginWidget && layout()) {
		layout()->removeWidget(m_pluginWidget);
		m_pluginWidget->setParent(nullptr);
	}
}

void VstEffectControlDialog::togglePluginUI( bool checked )
{
	if( !m_plugin ) {
		return;
	}

	if ( m_togglePluginButton->isChecked() != checked ) {
		m_togglePluginButton->setChecked( checked );
	}

	if ( checked ) {
		m_plugin->showUI();
	} else {
		m_plugin->hideUI();
	}
}


code:
/*
 * VstEffectControlDialog.h - dialog for displaying GUI of VST-effect-plugin
 *
 * Copyright (c) 2006-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#ifndef _VST_EFFECT_CONTROL_DIALOG_H
#define _VST_EFFECT_CONTROL_DIALOG_H

#include "EffectControlDialog.h"
#include "VstPlugin.h"

#include <QObject>
#include <QPainter>
#include <QLabel>
#include <QSharedPointer>

class VstEffectControls;
class PixmapButton;
class QPixmap;
class QPushButton;
class PixmapButton;

class VstEffectControlDialog : public EffectControlDialog
{
	Q_OBJECT
public:
	VstEffectControlDialog( VstEffectControls * _controls );
	virtual ~VstEffectControlDialog();

protected:
	void paintEvent( QPaintEvent * _pe ) override;
	void showEvent( QShowEvent* _se ) override;

private:
	QWidget * m_pluginWidget;

	QPushButton * m_togglePluginButton;
	PixmapButton * m_openPresetButton;
	PixmapButton * m_rolLPresetButton;
	PixmapButton * m_rolRPresetButton;
	PixmapButton * m_managePluginButton;
	PixmapButton * m_savePresetButton;

	QSharedPointer<VstPlugin> m_plugin;

	QLabel * tbLabel;

public slots:
	void togglePluginUI( bool checked );
} ;

#endif


code:
/*
 * VstEffectControls.cpp - controls for VST effect plugins
 *
 * Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QDomElement>

#include "VstEffectControls.h"
#include "VstEffect.h"

#include "LocaleHelper.h"
#include "MainWindow.h"
#include "GuiApplication.h"
#include <QMdiArea>
#include <QApplication>

VstEffectControls::VstEffectControls( VstEffect * _eff ) :
	EffectControls( _eff ),
	m_effect( _eff ),
	m_subWindow( NULL ),
	knobFModel( NULL ),
	ctrHandle( NULL ),
	lastPosInMenu (0),
	m_vstGuiVisible ( true )
//	m_presetLabel ( NULL )
{
}

VstEffectControls::~VstEffectControls()
{
	delete ctrHandle;
	ctrHandle = NULL;
}

void VstEffectControls::loadSettings( const QDomElement & _this )
{
	//m_effect->closePlugin();
	//m_effect->openPlugin( _this.attribute( "plugin" ) );
	m_effect->m_pluginMutex.lock();
	if( m_effect->m_plugin != NULL )
	{
		m_vstGuiVisible = _this.attribute( "guivisible" ).toInt();

		m_effect->m_plugin->loadSettings( _this );

		const QMap<QString, QString> & dump = m_effect->m_plugin->parameterDump();
		paramCount = dump.size();
		char paramStr[35];
		knobFModel = new FloatModel *[ paramCount ];
		QStringList s_dumpValues;
		for( int i = 0; i < paramCount; i++ )
		{
			sprintf( paramStr, "param%d", i );
			s_dumpValues = dump[ paramStr ].split( ":" );

			knobFModel[i] = new FloatModel( 0.0f, 0.0f, 1.0f, 0.01f, this, QString::number(i) );
			knobFModel[i]->loadSettings( _this, paramStr );

			if( !( knobFModel[ i ]->isAutomated() ||
						knobFModel[ i ]->controllerConnection() ) )
			{
				knobFModel[ i ]->setValue(LocaleHelper::toFloat(s_dumpValues.at(2)));
				knobFModel[ i ]->setInitValue(LocaleHelper::toFloat(s_dumpValues.at(2)));
			}

			connect( knobFModel[i], &FloatModel::dataChanged, this,
				[this, i]() { setParameter( knobFModel[i] ); }, Qt::DirectConnection);
		}

	}
	m_effect->m_pluginMutex.unlock();
}

void VstEffectControls::setParameter( Model * action )
{
	int knobUNID = action->displayName().toInt();

	if ( m_effect->m_plugin != NULL ) {
		m_effect->m_plugin->setParam( knobUNID, knobFModel[knobUNID]->value() );
	}
}

void VstEffectControls::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	_this.setAttribute( "plugin", m_effect->m_key.attributes["file"] );
	m_effect->m_pluginMutex.lock();
	if( m_effect->m_plugin != NULL )
	{
		m_effect->m_plugin->saveSettings( _doc, _this );
		if (knobFModel != NULL) {
			const QMap<QString, QString> & dump = m_effect->m_plugin->parameterDump();
			paramCount = dump.size();
			char paramStr[35];
			for( int i = 0; i < paramCount; i++ )
			{
				if (knobFModel[i]->isAutomated() || knobFModel[i]->controllerConnection()) {
					sprintf( paramStr, "param%d", i);
					knobFModel[i]->saveSettings( _doc, _this, paramStr );
				}
			}
		}
	}
	m_effect->m_pluginMutex.unlock();
}

int VstEffectControls::controlCount()
{
	return m_effect->m_plugin != NULL ? 1 : 0;
}

EffectControlDialog *VstEffectControls::createView()
{
	auto dialog = new VstEffectControlDialog( this );
	dialog->togglePluginUI( m_vstGuiVisible );
	return dialog;
}

void VstEffectControls::managePlugin( void )
{
	if ( m_effect->m_plugin != NULL && m_subWindow == NULL ) {
		manageVSTEffectView * tt = new manageVSTEffectView( m_effect, this);
		ctrHandle = (QObject *)tt;
	} else if (m_subWindow != NULL) {
		if (m_subWindow->widget()->isVisible() == false ) {
			m_scrollArea->show();
			m_subWindow->show();
		} else {
			m_scrollArea->hide();
			m_subWindow->hide();
		}
	}
}

void VstEffectControls::savePreset( void )
{

	if ( m_effect->m_plugin != NULL ) {
		m_effect->m_plugin->savePreset( );
/*    		bool converted;
    		QString str = m_vi->m_plugin->currentProgramName().section("/", 0, 0);
     		if (str != "")
   			lastPosInMenu = str.toInt(&converted, 10) - 1;
		QWidget::update();*/
	}

}

void VstEffectControls::updateMenu( void )
{

	// get all presets -
	if ( m_effect->m_plugin != NULL )
	{
		m_effect->m_plugin->loadProgramNames();
		///QWidget::update();

     		QString str = m_effect->m_plugin->allProgramNames();

    		QStringList list1 = str.split("|");

     		QMenu * to_menu = m_selPresetButton->menu();
    		to_menu->clear();

     		for (int i = 0; i < list1.size(); i++) {
			QAction* presetAction = new QAction(this);
			connect(presetAction, SIGNAL(triggered()), this, SLOT(selPreset()));

        		presetAction->setText(QString("%1. %2").arg(QString::number(i+1), list1.at(i)));
        		presetAction->setData(i);
			if (i == lastPosInMenu) {
        			presetAction->setIcon(embed::getIconPixmap( "sample_file", 16, 16 ));
			} else  presetAction->setIcon(embed::getIconPixmap( "edit_copy", 16, 16 ));
			to_menu->addAction( presetAction );
     		}

	}

}

void VstEffectControls::openPreset( void )
{

	if ( m_effect->m_plugin != NULL ) {
		m_effect->m_plugin->openPreset( );
    		bool converted;
    		QString str = m_effect->m_plugin->currentProgramName().section("/", 0, 0);
     		if (str != "")
   			lastPosInMenu = str.toInt(&converted, 10) - 1;
		//QWidget::update();
	}

}

void VstEffectControls::rollPreset( void )
{

	if ( m_effect->m_plugin != NULL ) {
		m_effect->m_plugin->rotateProgram( 1 );
    		bool converted;
    		QString str = m_effect->m_plugin->currentProgramName().section("/", 0, 0);
     		if (str != "")
   			lastPosInMenu = str.toInt(&converted, 10) - 1;
		//QWidget::update();
	}
}

void VstEffectControls::rolrPreset( void )
{

	if ( m_effect->m_plugin != NULL ) {
		m_effect->m_plugin->rotateProgram( -1 );
    		bool converted;
    		QString str = m_effect->m_plugin->currentProgramName().section("/", 0, 0);
     		if (str != "")
   			lastPosInMenu = str.toInt(&converted, 10) - 1;
		//QWidget::update();
	}
}

void VstEffectControls::selPreset( void )
{

     QAction *action = qobject_cast<QAction *>(sender());
     if (action)
         if ( m_effect->m_plugin != NULL ) {
		lastPosInMenu = action->data().toInt();
		m_effect->m_plugin->setProgram( lastPosInMenu );
		//QWidget::update();
	 }
}

void VstEffectControls::paintEvent( QPaintEvent * )
{

}

manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * m_vi ) :
	m_effect( _eff )
{
	m_vi2 = m_vi;
	widget = new QWidget();
        m_vi->m_scrollArea = new QScrollArea( widget );
	l = new QGridLayout( widget );

	m_vi->m_subWindow = gui->mainWindow()->addWindowedWidget(NULL, Qt::SubWindow |
			Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
	m_vi->m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
	m_vi->m_subWindow->setFixedSize( 960, 300);
	m_vi->m_subWindow->setWidget(m_vi->m_scrollArea);
	m_vi->m_subWindow->setWindowTitle( _eff->m_plugin->name() + tr( " - VST parameter control" ) );
	m_vi->m_subWindow->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) );
	m_vi->m_subWindow->setAttribute(Qt::WA_DeleteOnClose, false);

	l->setContentsMargins( 20, 10, 10, 10 );
	l->setVerticalSpacing( 10 );
	l->setHorizontalSpacing( 23 );

	m_syncButton = new QPushButton( tr( "VST sync" ), widget );
	connect( m_syncButton, SIGNAL( clicked() ), this,
							SLOT( syncPlugin() ) );

	l->addWidget( m_syncButton, 0, 0, 1, 2, Qt::AlignLeft );

	m_displayAutomatedOnly = new QPushButton( tr( "Automated" ), widget );
	connect( m_displayAutomatedOnly, SIGNAL( clicked() ), this,
							SLOT( displayAutomatedOnly() ) );

	l->addWidget( m_displayAutomatedOnly, 0, 1, 1, 2, Qt::AlignLeft );

	m_closeButton = new QPushButton( tr( "    Close    " ), widget );
	connect( m_closeButton, SIGNAL( clicked() ), this,
							SLOT( closeWindow() ) );

	l->addWidget( m_closeButton, 0, 2, 1, 7, Qt::AlignLeft );

	for( int i = 0; i < 10; i++ )
	{
		l->addItem( new QSpacerItem( 68, 45, QSizePolicy::Fixed, QSizePolicy::Fixed ), 0, i );
	}

	const QMap<QString, QString> & dump = m_effect->m_plugin->parameterDump();
	m_vi->paramCount = dump.size();

	vstKnobs = new CustomTextKnob *[ m_vi->paramCount ];

	bool hasKnobModel = true;
	if (m_vi->knobFModel == NULL) {
		m_vi->knobFModel = new FloatModel *[ m_vi->paramCount ];
		hasKnobModel = false;
	}

	char paramStr[35];
	QStringList s_dumpValues;

	for( int i = 0; i < m_vi->paramCount; i++ )
	{
		sprintf( paramStr, "param%d", i);
		s_dumpValues = dump[ paramStr ].split( ":" );

		vstKnobs[ i ] = new CustomTextKnob( knobBright_26, widget, s_dumpValues.at( 1 ) );
		vstKnobs[ i ]->setDescription( s_dumpValues.at( 1 ) + ":" );
		vstKnobs[ i ]->setLabel( s_dumpValues.at( 1 ).left( 15 ) );

		if( !hasKnobModel )
		{
			sprintf( paramStr, "%d", i);
			m_vi->knobFModel[ i ] = new FloatModel( LocaleHelper::toFloat(s_dumpValues.at(2)),
					0.0f, 1.0f, 0.01f, _eff, paramStr );
		}

		FloatModel * model = m_vi->knobFModel[i];
		connect( model, &FloatModel::dataChanged, this,
			[this, model]() { setParameter( model ); }, Qt::DirectConnection);
		vstKnobs[ i ] ->setModel( model );
	}
	syncParameterText();

	int i = 0;
	for( int lrow = 1; lrow < ( int( m_vi->paramCount / 10 ) + 1 ) + 1; lrow++ )
	{
		for( int lcolumn = 0; lcolumn < 10; lcolumn++ )
		{
			if( i < m_vi->paramCount )
			{
				l->addWidget( vstKnobs[i], lrow, lcolumn, Qt::AlignCenter );
			}
			i++;
		}
	}

	l->setRowStretch( ( int( m_vi->paramCount / 10 ) + 1 ), 1 );
	l->setColumnStretch( 10, 1 );

	widget->setLayout(l);
	widget->setAutoFillBackground(true);

	m_vi->m_scrollArea->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn );
	m_vi->m_scrollArea->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
	m_vi->m_scrollArea->setPalette( QApplication::palette( m_vi->m_scrollArea ) );
	m_vi->m_scrollArea->setMinimumHeight( 64 );

	m_vi->m_scrollArea->setWidget( widget );

	m_vi->m_subWindow->show();
}

void manageVSTEffectView::closeWindow()
{
	m_vi2->m_subWindow->hide();
}

void manageVSTEffectView::syncPlugin( void )
{
	char paramStr[35];
	QStringList s_dumpValues;
	const QMap<QString, QString> & dump = m_effect->m_plugin->parameterDump();
	float f_value;

	for( int i = 0; i < m_vi2->paramCount; i++ )
	{
		// only not automated knobs are synced from VST
		// those auto-setted values are not jurnaled, tracked for undo / redo
		if( !( m_vi2->knobFModel[ i ]->isAutomated() ||
					m_vi2->knobFModel[ i ]->controllerConnection() ) )
		{
			sprintf( paramStr, "param%d", i );
    			s_dumpValues = dump[ paramStr ].split( ":" );
			f_value = LocaleHelper::toFloat(s_dumpValues.at(2));
			m_vi2->knobFModel[ i ]->setAutomatedValue( f_value );
			m_vi2->knobFModel[ i ]->setInitValue( f_value );
		}
	}
	syncParameterText();
}

void manageVSTEffectView::displayAutomatedOnly( void )
{
	bool isAuto = QString::compare( m_displayAutomatedOnly->text(), tr( "Automated" ) ) == 0;

	for( int i = 0; i< m_vi2->paramCount; i++ )
	{

		if( !( m_vi2->knobFModel[ i ]->isAutomated() ||
					m_vi2->knobFModel[ i ]->controllerConnection() ) )
		{
			if( vstKnobs[ i ]->isVisible() == true  && isAuto )
			{
				vstKnobs[ i ]->hide();
				m_displayAutomatedOnly->setText( "All" );
			} else {
				vstKnobs[ i ]->show();
				m_displayAutomatedOnly->setText( "Automated" );
			}
		}
 	}
}

void manageVSTEffectView::setParameter( Model * action )
{
	int knobUNID = action->displayName().toInt();

	if ( m_effect->m_plugin != NULL ) {
		m_effect->m_plugin->setParam( knobUNID, m_vi2->knobFModel[knobUNID]->value() );
		syncParameterText();
	}
}

void manageVSTEffectView::syncParameterText()
{
	m_effect->m_plugin->loadParameterLabels();
	m_effect->m_plugin->loadParameterDisplays();

	QString paramLabelStr   = m_effect->m_plugin->allParameterLabels();
	QString paramDisplayStr = m_effect->m_plugin->allParameterDisplays();

	QStringList paramLabelList;
	QStringList paramDisplayList;

	for( int i = 0; i < paramLabelStr.size(); )
	{
		const int length = paramLabelStr[i].digitValue();
		paramLabelList.append(paramLabelStr.mid(i + 1, length));
		i += length + 1;
	}

	for( int i = 0; i < paramDisplayStr.size(); )
	{
		const int length = paramDisplayStr[i].digitValue();
		paramDisplayList.append(paramDisplayStr.mid(i + 1, length));
		i += length + 1;
	}

	for( int i = 0; i < paramLabelList.size(); ++i )
	{
		vstKnobs[i]->setValueText(paramDisplayList[i] + ' ' + paramLabelList[i]);
	}
}

manageVSTEffectView::~manageVSTEffectView()
{
	if( m_vi2->knobFModel != NULL )
	{
		for( int i = 0; i < m_vi2->paramCount; i++ )
		{
			delete m_vi2->knobFModel[ i ];
			delete vstKnobs[ i ];
		}
	}

	if( vstKnobs != NULL )
	{
		delete [] vstKnobs;
		vstKnobs = NULL;
	}

	if( m_vi2->knobFModel != NULL )
	{
		delete [] m_vi2->knobFModel;
		m_vi2->knobFModel = NULL;
	}

	if( m_vi2->m_scrollArea != NULL )
	{
		delete m_vi2->m_scrollArea;
		m_vi2->m_scrollArea = NULL;
	}

	if( m_vi2->m_subWindow != NULL )
	{
		m_vi2->m_subWindow->setAttribute( Qt::WA_DeleteOnClose );
		m_vi2->m_subWindow->close();

		if( m_vi2->m_subWindow != NULL )
		{
			delete m_vi2->m_subWindow;
		}
		m_vi2->m_subWindow = NULL;
	}
	//delete m_vi2->m_subWindow;
	//m_vi2->m_subWindow = NULL;
}


code:
/*
 * VstEffectControls.h - controls for VST effect plugins
 *
 * Copyright (c) 2008-2011 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#ifndef _VST_EFFECT_CONTROLS_H
#define _VST_EFFECT_CONTROLS_H

#include "EffectControls.h"
#include "VstEffectControlDialog.h"

#include <QMenu>
#include "embed.h"
#include <QPushButton>

#include <QMdiSubWindow>
#include <QScrollArea>
#include "CustomTextKnob.h"
#include <QLayout>
#include <QPainter>
#include <QObject>

class VstEffect;

class VstEffectControls : public EffectControls
{
	Q_OBJECT
public:
	VstEffectControls( VstEffect * _eff );
	virtual ~VstEffectControls();

	virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
	virtual void loadSettings( const QDomElement & _this );
	inline virtual QString nodeName() const
	{
		return "vsteffectcontrols";
	}

	virtual int controlCount();

	virtual EffectControlDialog * createView();

protected slots:
	void updateMenu( void );
	void managePlugin( void );
	void openPreset( void );
	void savePreset( void );
	void rollPreset( void );
	void rolrPreset( void );
	void selPreset( void );
	void setParameter( Model * action );

protected:
	virtual void paintEvent( QPaintEvent * _pe );

private:
	VstEffect * m_effect;

	QPushButton * m_selPresetButton;

	QMdiSubWindow * m_subWindow;
	QScrollArea * m_scrollArea;
	FloatModel ** knobFModel;
	int paramCount;

	QObject * ctrHandle;

	int lastPosInMenu;
//	QLabel * m_presetLabel;

	friend class VstEffectControlDialog;
	friend class manageVSTEffectView;

	bool m_vstGuiVisible;
} ;

class manageVSTEffectView : public QObject
{
	Q_OBJECT
public:
	manageVSTEffectView( VstEffect * _eff, VstEffectControls * m_vi );
	virtual ~manageVSTEffectView();

protected slots:
	void syncPlugin( void );
	void displayAutomatedOnly( void );
	void setParameter( Model * action );
	void syncParameterText();
	void closeWindow();

private:

//	static QPixmap * s_artwork;

	VstEffectControls * m_vi2;

	VstEffect * m_effect;

	QWidget *widget;
	QGridLayout * l;

	QPushButton * m_syncButton;
	QPushButton * m_displayAutomatedOnly;
	QPushButton * m_closeButton;
	CustomTextKnob ** vstKnobs;

} ;

#endif


code:
/*
 * VstSubPluginFeatures.cpp - derivation from
 *                            Plugin::Descriptor::SubPluginFeatures for
 *                            hosting VST-plugins
 *
 * Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QDir>
#include <QLabel>

#include "VstSubPluginFeatures.h"
#include "ConfigManager.h"

VstSubPluginFeatures::VstSubPluginFeatures( Plugin::PluginTypes _type ) :
	SubPluginFeatures( _type )
{
}

void VstSubPluginFeatures::fillDescriptionWidget( QWidget * _parent,
													const Key * _key  ) const
{
	new QLabel( QWidget::tr( "Name: " ) + _key->name, _parent );
	new QLabel( QWidget::tr( "File: " ) + _key->attributes["file"], _parent );
}

void VstSubPluginFeatures::listSubPluginKeys( const Plugin::Descriptor * _desc,
														KeyList & _kl ) const
{
	QStringList *dlls = new QStringList();
	const QString path = QString("");
	addPluginsFromDir(dlls, path );
	// TODO: eval m_type
	for( QStringList::ConstIterator it = dlls->begin();
							it != dlls->end(); ++it )
	{
		EffectKey::AttributeMap am;
		am["file"] = *it;
		_kl.push_back( Key( _desc, QFileInfo( *it ).baseName(), am ) );
	}
	delete dlls;
}

void VstSubPluginFeatures::addPluginsFromDir( QStringList* filenames, QString path ) const
{
	QStringList dirs = QDir ( ConfigManager::inst()->vstDir() + path ).
				entryList( QStringList() << "*" ,
						   QDir::Dirs, QDir::Name );
	for( int i = 0; i < dirs.size(); i++ )
	{
		if( dirs.at( i )[0] != '.' )
		{
			addPluginsFromDir( filenames, path+QDir::separator() + dirs.at( i ) );
		}
	}
	QStringList dlls = QDir( ConfigManager::inst()->vstDir() + path ).
				entryList( QStringList() << "*.dll",
						QDir::Files, QDir::Name );
	for( int i = 0; i < dlls.size(); i++ )
	{
		QString fName = path + QDir::separator() + dlls.at( i );
		fName.remove( 0, 1 );
		filenames->append( fName );
	}
}


code:
/*
 * VstSubPluginFeatures.h - derivation from
 *                          Plugin::Descriptor::SubPluginFeatures for
 *                          hosting VST-plugins
 *
 * Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#ifndef _VST_SUBPLUGIN_FEATURES_H
#define _VST_SUBPLUGIN_FEATURES_H

#include "Effect.h"

class VstSubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures
{
public:
	VstSubPluginFeatures( Plugin::PluginTypes _type );

	virtual void fillDescriptionWidget( QWidget * _parent,
											const Key * _key ) const;

	virtual void listSubPluginKeys( const Plugin::Descriptor * _desc,
											KeyList & _kl ) const;
private:
	void addPluginsFromDir(QStringList* filenames,  QString path) const;
} ;

#endif


code:
INCLUDE(BuildPlugin)

INCLUDE_DIRECTORIES(exprtk)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_sc_andor")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_return_statement")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_break_continue")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_comments")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_string_capabilities")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_rtl_io_file")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_rtl_vecops")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WERROR_FLAGS} -fexceptions")

IF(LMMS_BUILD_WIN32 AND NOT MSVC)
	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj -Dexprtk_disable_enhanced_features")
ELSEIF(LMMS_BUILD_WIN32 AND MSVC)
	SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
ENDIF()

BUILD_PLUGIN(xpressive
	Xpressive.cpp
	ExprSynth.cpp
	Xpressive.h
	exprtk/exprtk.hpp
	MOCFILES Xpressive.h
	EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png"
)