aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEddy Pedroni <epedroni@pm.me>2024-11-10 09:39:29 +0100
committerEddy Pedroni <epedroni@pm.me>2024-11-10 09:39:29 +0100
commit85b0116f80515c71e0250e4910c721b9e4fabca1 (patch)
treef046e140c13c3be744b041193e9100ea748c582b
parent7c00f418caf8f1e9fcb235563127a9c8cea22bb4 (diff)
Set A and B limits via MIDI controller
-rw-r--r--doc/diagram.drawio572
-rw-r--r--doc/known-issues.md2
-rw-r--r--solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py41
-rw-r--r--solo-tool-project/test/midi_launchpad_mini_integrationtest.py60
4 files changed, 654 insertions, 21 deletions
diff --git a/doc/diagram.drawio b/doc/diagram.drawio
index e3d1846..51dd11f 100644
--- a/doc/diagram.drawio
+++ b/doc/diagram.drawio
@@ -1 +1,571 @@
-<mxfile host="app.diagrams.net" modified="2022-02-03T17:57:29.948Z" agent="5.0 (X11)" etag="JSVR_wePj1eIi_OTfR3Q" version="16.5.3" type="device" pages="3"><diagram id="g-wcGVps3MkI6_XAwNEs" name="Core">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</diagram><diagram id="yK3rgzEW7m2RTtpwjvJ6" name="MIDI">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</diagram><diagram id="PyNSc7ezSt42GBdjTBvd" name="Launchpad">5ZxNc6M4EIZ/jY9bRUt8SMdMNjNzmLlsDlO7N2Jkm1lsuTD+yP76xTY4RC1XkSrbTTunQEuB8LZCP91Aj+TjfPetTJeznzYzxUgE2W4k/xwJAaEI6h97y+vRorQ4GqZlnjWT3gzP+X+mMTa/N13nmVm9m1hZW1T58r1xbBcLM67e2dKytNv30ya2eH/WZTo1yPA8Tgts/ZVn1ay5CpG82b+bfDprzwyxPo7M03ZycyWrWZrZbcckn0bysbS2Om7Nd4+m2IvX6vJP9XtinuR6O/lr9XW2+DX58fePP44H+/qRXzldQmkW1WUPLZuL3aTF2pwuLS7q03x5Keut6X5rY4v13GB7VM+NGnGq11bx0q4XmdmfFeo521lemedlOt6Pbus1Vttm1bxohmu3V2m+MGW9H9T7k7woHm1hy8OxZBYZlYW1fVWV9l/TGVHiRcZxPdJTnEbEjSkrs+ssjUasb8bOTVW+1lOa0bBxe7Puk2Z321lEjWnWWT+tLW2W7fR03DfX1BuNdz7gqfbP6XgKYuyS1dLU0hN4ajIx8Xjs81SW6JcguJ2nIEqIXQXIVVJcVvwbSSnCiFhKgaQMFUspZbsEyKSUSMo4ZCllCJpYyhBJ2Z6LmZRREBNLGSEptS+sVXZ53fClxsYfvl5UFEa3dIkOiV0SY9IAgX2yLNLX/ZnwQLpemU8CG7ECWm8pfC+C/gQf3zPBgxwWwit8r4OkN8Jf31WE/1Wuq8gZXuGboJQ8QryrJTnEqwRpGWqeWpJTvFJIy/jCd4VbaUmO8QoXv9pozk1Lco7XuDylE6ZaUgO4xvUjAKbBh5yPNS57eHKZc3yc3DMfn55sDISPtSeVUb35+PquIuRj11XkfKxxLiOZVBNdLcn5WONcI2JSTnS1JOdjjXONOOapJTkfa5xrKMFTS3o+xrmGZvIgC2lJzccQeJ6FA9PoQw7IEAgcyvsTsrpnQpYwLEKGAGcz+yjRE5Gv7ytCRHZ9RY7IEOB8RjKp1blikjMyBDjhiJgU61wxySEZApxxxEyqda6Y5JQMAU45FJNqnSsmOSZDgHOONo5l+aYNZPoU8+qzdOyeqb/X82XvyZWtT/2AA+e5A3yOSDoA4MfZEwDTUEoP/ICfL4T9gV/fM/CHwcCAH3By5ntD/wzwX99XpG9iDQ34AWdnkknx0RWTHvgBZ0+R4CkmPfADzp5iJuVHV0x64AecPSkm1UdXTHrgB5w9neq77NQkB2XA6dNpuXJTkx6U21J9NwT1B+XDMr5fUtYDI2WBsxrfm/BnSPkGzqJEZT00VBY4r5FMKpCumPSoLHDeETGpQLpi0qOywHlH2xKAm5j0qCxw3qEufJe9lZj0qCxw3uGpYUMAH6h3T6eF6T394csgSuMDoh564hee/AmYRtIBED9+0OD5Gvks8cPlIfJa2kdqYPzefjjQ5XfRn98ZS09P49LTsYRJedAVk57GW4Lt0jiT8qArJj2NS5zatHcqbmLS07j09C1hUhx0xaSncYlTm8OrtKgbRmk2uV2v8MjDld8goWtpgrxFjsnS19TE83HZeW+t7GL6WfxFD+ISP8jwtMo4C+KCDw3GydBAHOdAvg8CzoE4Y+npQdzT5bF1Bzcx6UHc04cxYroy6UHc04kxYfL+vSsmPYh7ejEqJjU7V0x6EPd0Yzx8J+VGpoXZVTeHcMJaNfIUOYR7mj2C7/M1v6euDuAD8tUVAbzefetZfhjrdH6XT/8D</diagram></mxfile> \ No newline at end of file
+<mxfile host="Electron" modified="2024-11-09T22:49:49.440Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.1.2 Chrome/114.0.5735.289 Electron/25.9.4 Safari/537.36" etag="Lsuc8pY-LfMLzD1PEqfs" version="22.1.2" type="device" pages="3">
+ <diagram id="g-wcGVps3MkI6_XAwNEs" name="Core">
+ <mxGraphModel dx="846" dy="395" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
+ <root>
+ <mxCell id="0" />
+ <mxCell id="1" parent="0" />
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-1" value="solo tool" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;glass=0;shadow=0;sketch=0;fillColor=none;align=left;verticalAlign=top;" parent="1" vertex="1">
+ <mxGeometry x="410" y="227" width="530" height="693" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;" parent="1" source="718ck8ZuCs3BOJF-nClt-3" target="MU1YSbBTE73kW5gn9q-V-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-6" value="&lt;div&gt;Current&lt;/div&gt;&lt;div&gt;position&lt;br&gt;&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="MU1YSbBTE73kW5gn9q-V-5" vertex="1" connectable="0">
+ <mxGeometry x="0.2328" y="-1" relative="1" as="geometry">
+ <mxPoint x="10" y="1" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-1" value="&lt;div&gt;media player&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
+ <mxGeometry x="440" y="648" width="160" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="MU1YSbBTE73kW5gn9q-V-11" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="300" y="508.0344827586207" as="sourcePoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-12" value="&lt;div&gt;Set current&lt;/div&gt;&lt;div&gt;song&lt;br&gt;&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="9fq4LfI0W2HX4gTsbRt6-3" vertex="1" connectable="0">
+ <mxGeometry x="0.2112" relative="1" as="geometry">
+ <mxPoint y="-47" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="9fq4LfI0W2HX4gTsbRt6-4" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-4" value="Play" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="260" y="525" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-7" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="9fq4LfI0W2HX4gTsbRt6-6" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-6" value="Pause" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="250" y="562" width="50" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="9fq4LfI0W2HX4gTsbRt6-8" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-8" value="Stop" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="260" y="598" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="9fq4LfI0W2HX4gTsbRt6-10" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-10" value="Set playback rate" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="190" y="635" width="110" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="9fq4LfI0W2HX4gTsbRt6-12" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="9fq4LfI0W2HX4gTsbRt6-12" value="Set playback position" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="170" y="671" width="130" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="MU1YSbBTE73kW5gn9q-V-1" target="5IB1TeDA8rQgVov2ilYq-2" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-1" value="Add a/b limit" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="840" y="68" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;" parent="1" source="MU1YSbBTE73kW5gn9q-V-3" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-3" value="a/b controller" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
+ <mxGeometry x="650" y="408" width="160" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="MU1YSbBTE73kW5gn9q-V-9" target="MU1YSbBTE73kW5gn9q-V-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-9" value="Enable a/b mode" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="830" y="128" width="110" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="UmMSCIYVAIiNOvlXGdHZ-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="MU1YSbBTE73kW5gn9q-V-11" target="MU1YSbBTE73kW5gn9q-V-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-11" value="playlist" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
+ <mxGeometry x="440" y="408" width="160" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="MU1YSbBTE73kW5gn9q-V-13" target="5IB1TeDA8rQgVov2ilYq-2" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-13" value="Add song" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="360" y="98" width="70" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="MU1YSbBTE73kW5gn9q-V-15" target="MU1YSbBTE73kW5gn9q-V-11" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-15" value="Choose song" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="340" y="128" width="90" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="MU1YSbBTE73kW5gn9q-V-17" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-17" value="Set volume" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="220" y="708" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="MU1YSbBTE73kW5gn9q-V-22" target="MU1YSbBTE73kW5gn9q-V-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="MU1YSbBTE73kW5gn9q-V-22" value="Choose a/b limit" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="840" y="98" width="100" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="718ck8ZuCs3BOJF-nClt-4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="9fq4LfI0W2HX4gTsbRt6-1" target="718ck8ZuCs3BOJF-nClt-3" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="760.3103448275863" y="608" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="718ck8ZuCs3BOJF-nClt-5" value="&lt;div&gt;Current&lt;/div&gt;&lt;div&gt;position&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="718ck8ZuCs3BOJF-nClt-4" vertex="1" connectable="0">
+ <mxGeometry x="0.2833" relative="1" as="geometry">
+ <mxPoint x="-23" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="718ck8ZuCs3BOJF-nClt-6" value="&lt;div&gt;Set current&lt;/div&gt;&lt;div&gt;position&lt;br&gt;&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="718ck8ZuCs3BOJF-nClt-4" vertex="1" connectable="0">
+ <mxGeometry x="0.2833" relative="1" as="geometry">
+ <mxPoint x="-46" y="-120" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="718ck8ZuCs3BOJF-nClt-3" value="tick" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="750" y="648" width="160" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.25;exitY=1;exitDx=0;exitDy=0;" parent="1" source="5IB1TeDA8rQgVov2ilYq-2" target="MU1YSbBTE73kW5gn9q-V-11" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-4" value="Add song" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="5IB1TeDA8rQgVov2ilYq-3" vertex="1" connectable="0">
+ <mxGeometry x="0.2933" y="3" relative="1" as="geometry">
+ <mxPoint x="22" y="-4" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="5IB1TeDA8rQgVov2ilYq-2" target="MU1YSbBTE73kW5gn9q-V-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-6" value="&lt;div&gt;Add a/b limit&lt;br&gt;&lt;/div&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="5IB1TeDA8rQgVov2ilYq-5" vertex="1" connectable="0">
+ <mxGeometry x="0.2833" y="-2" relative="1" as="geometry">
+ <mxPoint x="-32" y="-7" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-2" value="&lt;div&gt;session&lt;/div&gt;&lt;div&gt;manager&lt;br&gt;&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
+ <mxGeometry x="550" y="258" width="160" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="5IB1TeDA8rQgVov2ilYq-14" target="5IB1TeDA8rQgVov2ilYq-2" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-14" value="Load session" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="445" y="58" width="90" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="5IB1TeDA8rQgVov2ilYq-15" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="630" y="258" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="5IB1TeDA8rQgVov2ilYq-15" value="Save session" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="445" y="28" width="90" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="PYF8YKytvgJsIEJjpRti-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="PYF8YKytvgJsIEJjpRti-1" target="718ck8ZuCs3BOJF-nClt-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="PYF8YKytvgJsIEJjpRti-1" value="Tick" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="700" y="940" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="ofcqv09syQELO3cvxpxf-1" target="MU1YSbBTE73kW5gn9q-V-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-1" value="Next a/b limit" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="830" y="160" width="100" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="ofcqv09syQELO3cvxpxf-2" target="MU1YSbBTE73kW5gn9q-V-3" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-2" value="Previous a/b limit" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="830" y="190" width="120" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="ofcqv09syQELO3cvxpxf-5" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-5" value="Jump to A" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="230" y="737" width="70" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="ofcqv09syQELO3cvxpxf-7" target="MU1YSbBTE73kW5gn9q-V-11" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-7" value="Next song" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="350" y="160" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="ofcqv09syQELO3cvxpxf-8" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="520" y="410" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-8" value="Previous song" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="330" y="190" width="100" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="1" source="ofcqv09syQELO3cvxpxf-11" target="9fq4LfI0W2HX4gTsbRt6-1" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-15" value="playing state&lt;br&gt;callback" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="ofcqv09syQELO3cvxpxf-14" vertex="1" connectable="0">
+ <mxGeometry x="0.2283" y="-2" relative="1" as="geometry">
+ <mxPoint x="22" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-11" value="&lt;div&gt;notifier&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
+ <mxGeometry x="505" y="790" width="160" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="ofcqv09syQELO3cvxpxf-12" target="ofcqv09syQELO3cvxpxf-11" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="ofcqv09syQELO3cvxpxf-12" value="Register playing state callback" style="text;html=1;align=right;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
+ <mxGeometry x="110" y="820" width="200" height="20" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="yK3rgzEW7m2RTtpwjvJ6" name="MIDI">
+ <mxGraphModel dx="2015" dy="395" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
+ <root>
+ <mxCell id="OKDEixDBbmxQMGRGU1jO-0" />
+ <mxCell id="OKDEixDBbmxQMGRGU1jO-1" parent="OKDEixDBbmxQMGRGU1jO-0" />
+ <mxCell id="KjrEduvjUaLFBeyMDJhb-19" value="" style="endArrow=classic;html=1;rounded=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" parent="OKDEixDBbmxQMGRGU1jO-1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="-270" y="247" as="sourcePoint" />
+ <mxPoint x="-110" y="247" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="KjrEduvjUaLFBeyMDJhb-20" value="" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=0.75;exitDx=0;exitDy=0;" parent="OKDEixDBbmxQMGRGU1jO-1" edge="1">
+ <mxGeometry width="50" height="50" relative="1" as="geometry">
+ <mxPoint x="-110" y="280" as="sourcePoint" />
+ <mxPoint x="-270" y="280.5" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="KjrEduvjUaLFBeyMDJhb-21" value="MIDI bus" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="OKDEixDBbmxQMGRGU1jO-1" vertex="1">
+ <mxGeometry x="-230" y="255" width="70" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="KjrEduvjUaLFBeyMDJhb-22" value="Device" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="OKDEixDBbmxQMGRGU1jO-1" vertex="1">
+ <mxGeometry x="-430" y="214.5" width="140" height="105.5" as="geometry" />
+ </mxCell>
+ <mxCell id="cDpx_x92aZCDt8U1TkIR-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;" parent="OKDEixDBbmxQMGRGU1jO-1" source="fBglSRjiR8ACvM9LEDBr-1" target="fBglSRjiR8ACvM9LEDBr-3" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="80" y="294" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="cDpx_x92aZCDt8U1TkIR-3" value="callback" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="cDpx_x92aZCDt8U1TkIR-2" vertex="1" connectable="0">
+ <mxGeometry x="-0.2773" y="-1" relative="1" as="geometry">
+ <mxPoint x="10" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="fBglSRjiR8ACvM9LEDBr-1" value="mido" style="rounded=0;whiteSpace=wrap;html=1;" parent="OKDEixDBbmxQMGRGU1jO-1" vertex="1">
+ <mxGeometry x="-100" y="200" width="80" height="134.5" as="geometry" />
+ </mxCell>
+ <mxCell id="cDpx_x92aZCDt8U1TkIR-4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;startArrow=classic;startFill=1;endArrow=none;endFill=0;" parent="OKDEixDBbmxQMGRGU1jO-1" source="fBglSRjiR8ACvM9LEDBr-3" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="370" y="232.8888888888889" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="cDpx_x92aZCDt8U1TkIR-5" value="Set mapping" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="cDpx_x92aZCDt8U1TkIR-4" vertex="1" connectable="0">
+ <mxGeometry x="-0.2097" y="-2" relative="1" as="geometry">
+ <mxPoint x="17" y="-2" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="cDpx_x92aZCDt8U1TkIR-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;startArrow=none;startFill=0;endArrow=classic;endFill=1;" parent="OKDEixDBbmxQMGRGU1jO-1" source="fBglSRjiR8ACvM9LEDBr-3" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="360" y="300.66666666666663" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="cDpx_x92aZCDt8U1TkIR-7" value="Play/pause/stop&lt;br&gt;etc" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="cDpx_x92aZCDt8U1TkIR-6" vertex="1" connectable="0">
+ <mxGeometry x="-0.26" y="-2" relative="1" as="geometry">
+ <mxPoint x="14" y="-21" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-0" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;startArrow=classic;startFill=1;endArrow=none;endFill=0;" parent="OKDEixDBbmxQMGRGU1jO-1" source="fBglSRjiR8ACvM9LEDBr-3" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="360" y="267.33333333333326" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="fBglSRjiR8ACvM9LEDBr-3" value="midi&lt;br&gt;interface" style="rounded=0;whiteSpace=wrap;html=1;" parent="OKDEixDBbmxQMGRGU1jO-1" vertex="1">
+ <mxGeometry x="90" y="199.5" width="120" height="135.5" as="geometry" />
+ </mxCell>
+ <mxCell id="cDpx_x92aZCDt8U1TkIR-0" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.25;entryDx=0;entryDy=0;exitX=0;exitY=0.25;exitDx=0;exitDy=0;" parent="OKDEixDBbmxQMGRGU1jO-1" source="fBglSRjiR8ACvM9LEDBr-3" target="fBglSRjiR8ACvM9LEDBr-1" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="80" y="241" as="sourcePoint" />
+ <mxPoint x="210" y="530" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="cDpx_x92aZCDt8U1TkIR-1" value="send" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="cDpx_x92aZCDt8U1TkIR-0" vertex="1" connectable="0">
+ <mxGeometry x="-0.168" y="5" relative="1" as="geometry">
+ <mxPoint x="-10" y="-5" as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-15" value="" style="group" parent="OKDEixDBbmxQMGRGU1jO-1" vertex="1" connectable="0">
+ <mxGeometry x="717" y="125" width="190" height="429" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-0" value="&lt;div&gt;SoloTool&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;glass=0;shadow=0;sketch=0;align=right;verticalAlign=top;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry width="190" height="429" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-7" value="Play" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="223" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-9" value="Pause" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="254" width="50" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-11" value="Stop" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="284" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-13" value="Set playback rate" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="315" width="110" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-15" value="Set playback position" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#666666;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="345" width="130" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-17" value="Add a/b limit" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#666666;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="71" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-21" value="Enable a/b mode" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="162" width="110" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-25" value="Add song" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#666666;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="101" width="70" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-27" value="Choose song" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="193" width="90" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-29" value="Set volume" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="376" width="80" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-31" value="Choose a/b limit" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="132" width="100" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-42" value="Load session" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#666666;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="40" width="90" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-44" value="Save session" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#666666;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="10" width="90" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="jLuthnc1TARuY79bHbOS-46" value="Tick" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#666666;" parent="FipM2rDAY6IncK5jYn7S-15" vertex="1">
+ <mxGeometry x="10" y="406" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-16" value="" style="group" parent="OKDEixDBbmxQMGRGU1jO-1" vertex="1" connectable="0">
+ <mxGeometry x="517" y="255" width="110" height="232" as="geometry" />
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-1" value="Play" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-16" vertex="1">
+ <mxGeometry y="91" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-2" value="Pause" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-16" vertex="1">
+ <mxGeometry y="122" width="50" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-3" value="Stop" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-16" vertex="1">
+ <mxGeometry y="152" width="40" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-4" value="Set playback rate" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-16" vertex="1">
+ <mxGeometry y="183" width="110" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-5" value="&lt;span style=&quot;color: rgb(0 , 0 , 0)&quot;&gt;Set volume&lt;/span&gt;" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#666666;" parent="FipM2rDAY6IncK5jYn7S-16" vertex="1">
+ <mxGeometry y="214" width="78" height="18" as="geometry" />
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-7" value="Enable a/b mode" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-16" vertex="1">
+ <mxGeometry y="30" width="110" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-9" value="Choose song" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-16" vertex="1">
+ <mxGeometry y="61" width="90" height="20" as="geometry" />
+ </mxCell>
+ <mxCell id="FipM2rDAY6IncK5jYn7S-11" value="Choose a/b limit" style="text;html=1;align=left;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="FipM2rDAY6IncK5jYn7S-16" vertex="1">
+ <mxGeometry width="100" height="20" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="PyNSc7ezSt42GBdjTBvd" name="Launchpad">
+ <mxGraphModel dx="846" dy="395" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
+ <root>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-0" />
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-1" parent="ZtjfeE3uwfRsFhnWfLYL-0" />
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-39" value="0&lt;br&gt;volume&lt;br&gt;50%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="40" y="80" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-40" value="16&lt;br&gt;speed&lt;br&gt;50%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="40" y="167" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-41" value="32" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="40" y="255" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-42" value="48&lt;br&gt;previous&lt;br&gt;song" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="40" y="342" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-43" value="64" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="40" y="429" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-44" value="80" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="40" y="516" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-45" value="96&lt;br&gt;stop" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="40" y="604" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-46" value="112&lt;br&gt;play/&lt;br&gt;pause" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="40" y="691" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-84" value="1&lt;br&gt;volume&lt;br&gt;60%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="130" y="80" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-85" value="17&lt;br&gt;speed&lt;br&gt;60%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="130" y="167" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-86" value="33" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="130" y="255" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-87" value="49&lt;br&gt;-1 min" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="130" y="342" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-88" value="65" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="130" y="429" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-89" value="81" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="130" y="516" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-90" value="97" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="130" y="604" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-91" value="113" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="130" y="691" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-93" value="2&lt;br&gt;volume&lt;br&gt;70%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="220" y="80" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-94" value="18&lt;br&gt;speed&lt;br&gt;70%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="220" y="167" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-95" value="34" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="220" y="255" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-96" value="50&lt;br&gt;-30 s" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="220" y="342" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-97" value="66" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="220" y="429" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-98" value="82" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="220" y="516" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-99" value="98&lt;br&gt;toggle AB" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="220" y="604" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-100" value="114&lt;br&gt;jump to A" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="220" y="691" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-102" value="3&lt;br&gt;volume&lt;br&gt;80%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="310" y="80" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-103" value="19&lt;br&gt;speed&lt;br&gt;80%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="310" y="167" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-104" value="35" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="310" y="255" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-105" value="51&lt;br&gt;-5 s" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="310" y="342" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-106" value="67" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="310" y="429" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-107" value="83" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="310" y="516" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-108" value="&lt;div&gt;99&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="310" y="604" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-109" value="115" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="310" y="691" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-111" value="4&lt;br&gt;volume&lt;br&gt;90%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="400" y="80" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-112" value="20&lt;br&gt;speed&lt;br&gt;90%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="400" y="167" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-113" value="36" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="400" y="255" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-114" value="52&lt;br&gt;+5 s" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="400" y="342" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-115" value="68" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="400" y="429" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-116" value="84" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="400" y="516" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-117" value="100" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="400" y="604" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-118" value="116&lt;br&gt;set A" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="400" y="691" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-120" value="5&lt;br&gt;volume&lt;br&gt;100%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="490" y="80" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-121" value="21&lt;br&gt;speed&lt;br&gt;100%" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="490" y="167" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-122" value="37" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="490" y="255" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-123" value="53&lt;br&gt;+30 s" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="490" y="342" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-124" value="69" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="490" y="429" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-125" value="85" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="490" y="516" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-126" value="&lt;div&gt;101&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="490" y="604" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-127" value="117&lt;br&gt;set B" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="490" y="691" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-129" value="6&lt;br&gt;volume&lt;br&gt;110%" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="580" y="80" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-130" value="22&lt;br&gt;speed&lt;br&gt;110%" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="580" y="167" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-131" value="38" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="580" y="255" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-132" value="54&lt;br&gt;+1 min" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="580" y="342" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-133" value="70" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="580" y="429" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-134" value="86" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="580" y="516" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-135" value="102" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="580" y="604" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-136" value="118&lt;br&gt;previous AB" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="580" y="691" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-138" value="7&lt;br&gt;volume&lt;br&gt;120%" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="670" y="80" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-139" value="23&lt;br&gt;speed&lt;br&gt;120%" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="670" y="167" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-140" value="39" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="670" y="255" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-141" value="55&lt;br&gt;next song" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="670" y="342" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-142" value="71" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="670" y="429" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-143" value="87" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="670" y="516" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-144" value="103" style="rounded=1;whiteSpace=wrap;html=1;container=0;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="670" y="604" width="80" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZtjfeE3uwfRsFhnWfLYL-145" value="119&lt;br&gt;next AB" style="rounded=1;whiteSpace=wrap;html=1;container=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="ZtjfeE3uwfRsFhnWfLYL-1" vertex="1">
+ <mxGeometry x="670" y="691" width="80" height="80" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+</mxfile>
diff --git a/doc/known-issues.md b/doc/known-issues.md
index e87a3fc..2a43f53 100644
--- a/doc/known-issues.md
+++ b/doc/known-issues.md
@@ -14,7 +14,6 @@
* MIDI controller feature requests:
* skip ahead and behind by steps of a few seconds
- * set A and B points at current play head position (during playback)
# Closed Issues
@@ -36,6 +35,7 @@
* CLI feature requests:
* close application without crashing
* wipe LED state when application closes
+ * set A and B points at current play head position (during playback)
* GUI feature requests:
* wipe LED state when application closes
diff --git a/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py b/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py
index 8f32650..ee77d21 100644
--- a/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py
+++ b/solo-tool-project/src/solo_tool/midi_controller_launchpad_mini.py
@@ -30,16 +30,21 @@ class MidiController:
self._soloTool.registerPlaybackRateCallback(self._updateRateRow)
self._soloTool.registerAbLimitEnabledCallback(self._updateToggleAbLimitEnableButton)
+ self._aLimit = 0.0
+ self._bLimit = 0.0
+
def _registerHandlers(self):
self._handlers = {
96 : self._soloTool.stop,
- 99 : self._soloTool.jumpToA,
+ 114 : self._soloTool.jumpToA,
112 : self._playPause,
- 101 : self._toggleAbLimitEnable,
- 102 : self._soloTool.previousStoredAbLimits,
- 103 : self._soloTool.nextStoredAbLimits,
- 118 : self._soloTool.previousSong,
- 119 : self._soloTool.nextSong
+ 98 : self._toggleAbLimitEnable,
+ 118 : self._soloTool.previousStoredAbLimits,
+ 119 : self._soloTool.nextStoredAbLimits,
+ 116 : self._setALimit,
+ 117 : self._setBLimit,
+ 48 : self._soloTool.previousSong,
+ 55 : self._soloTool.nextSong
}
for i in range(0, 8):
@@ -71,6 +76,14 @@ class MidiController:
else:
self._soloTool.play()
+ def _setALimit(self):
+ self._aLimit = self._soloTool.getPlaybackPosition()
+ self._soloTool.setAbLimits(self._aLimit, self._bLimit)
+
+ def _setBLimit(self):
+ self._bLimit = self._soloTool.getPlaybackPosition()
+ self._soloTool.setAbLimits(self._aLimit, self._bLimit)
+
def _toggleAbLimitEnable(self):
self._soloTool.setAbLimitEnable(not self._soloTool.isAbLimitEnabled())
@@ -82,9 +95,9 @@ class MidiController:
def _updateToggleAbLimitEnableButton(self, enabled):
if enabled:
- self._setButtonLED(6, 5, MidiController.LED_GREEN)
+ self._setButtonLED(6, 2, MidiController.LED_GREEN)
else:
- self._setButtonLED(6, 5, MidiController.LED_RED)
+ self._setButtonLED(6, 2, MidiController.LED_RED)
def _updateVolumeRow(self, volume):
t1 = int(round(volume / MidiController.PLAYBACK_VOLUME_STEP, 1))
@@ -138,11 +151,13 @@ class MidiController:
self._updateToggleAbLimitEnableButton(self._soloTool.isAbLimitEnabled())
# AB control
- self._setButtonLED(6, 3, MidiController.LED_YELLOW)
- self._setButtonLED(6, 6, MidiController.LED_RED)
- self._setButtonLED(6, 7, MidiController.LED_GREEN)
-
- # Song control
+ self._setButtonLED(7, 2, MidiController.LED_YELLOW)
self._setButtonLED(7, 6, MidiController.LED_RED)
self._setButtonLED(7, 7, MidiController.LED_GREEN)
+ self._setButtonLED(7, 4, MidiController.LED_YELLOW)
+ self._setButtonLED(7, 5, MidiController.LED_YELLOW)
+
+ # Song control
+ self._setButtonLED(3, 0, MidiController.LED_RED)
+ self._setButtonLED(3, 7, MidiController.LED_GREEN)
diff --git a/solo-tool-project/test/midi_launchpad_mini_integrationtest.py b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py
index 17649d4..e7c1315 100644
--- a/solo-tool-project/test/midi_launchpad_mini_integrationtest.py
+++ b/solo-tool-project/test/midi_launchpad_mini_integrationtest.py
@@ -9,14 +9,24 @@ LED_YELLOW = 126
LED_GREEN = 124
LED_OFF = 0
-nextSongButton = 119
-previousSongButton = 118
+nextSongButton = 55
+plus1MinuteButton = 54
+plus30SecondsButton = 53
+plus5SecondsButton = 52
+minus5SecondsButton = 51
+minus30SecondsButton = 50
+minus1MinuteButton = 49
+previousSongButton = 48
+
playPauseButton = 112
stopButton = 96
-nextLimitButton = 103
-previousLimitButton = 102
-abToggleButton = 101
-jumpToAButton = 99
+
+nextLimitButton = 119
+previousLimitButton = 118
+abToggleButton = 98
+jumpToAButton = 114
+setAButton = 116
+setBButton = 117
class MidiWrapperMock:
def __init__(self):
@@ -352,6 +362,8 @@ def test_connectDisconnect(uut, midiWrapperMock):
(jumpToAButton, LED_YELLOW, 0),
(previousLimitButton, LED_RED, 0),
(nextLimitButton, LED_GREEN, 0),
+ (setAButton, LED_YELLOW, 0),
+ (setBButton, LED_YELLOW, 0),
(previousSongButton, LED_RED, 0),
(nextSongButton, LED_GREEN, 0)
])
@@ -388,3 +400,39 @@ def test_playingFeedbackWhenChangingSong(uut, midiWrapperMock, soloTool, playerM
assert playerMock.state == PlayerMock.STOPPED
assert midiWrapperMock.getLatestMessage() == (playPauseButton, LED_YELLOW, 0)
+def test_setAbButtons(uut, midiWrapperMock, soloTool, playerMock):
+ song = "test.flac"
+ soloTool.addSong(song)
+ soloTool.setSong(0)
+ soloTool.setAbLimitEnable(True)
+ abLimits = (0.6, 0.8)
+ soloTool.storeAbLimits(abLimits[0], abLimits[1])
+
+ uut.connect()
+
+ def checkLimit(aLimit, bLimit):
+ playerMock.position = bLimit - 0.1
+ soloTool.tick()
+ assert playerMock.position == bLimit - 0.1
+
+ playerMock.position = bLimit + 0.1
+ soloTool.tick()
+ assert playerMock.position == aLimit
+
+ # Set A limit
+ playerMock.position = 0.3
+ midiWrapperMock.simulateInput(setAButton)
+ playerMock.position = 0.5
+ midiWrapperMock.simulateInput(jumpToAButton)
+
+ assert playerMock.position == 0.3
+
+ # Set B limit
+ playerMock.position = 0.4
+ midiWrapperMock.simulateInput(setBButton)
+ checkLimit(0.3, 0.4)
+
+ # Selecting preset overrides manually set limits
+ midiWrapperMock.simulateInput(nextLimitButton)
+ checkLimit(abLimits[0], abLimits[1])
+