aboutsummaryrefslogtreecommitdiffstats
path: root/doc
diff options
context:
space:
mode:
authorEddy Pedroni <epedroni@pm.me>2025-07-21 18:57:53 +0200
committerEddy Pedroni <epedroni@pm.me>2025-07-21 18:57:53 +0200
commitf0835be9ffcae8e5c74c8fc6a0da2f7bc49343ac (patch)
treefb7a6f34e9068b48b67c039e7fcead23e361da75 /doc
Initial commit, daemon hello world
Diffstat (limited to 'doc')
-rw-r--r--doc/architecture.drawio255
-rw-r--r--doc/devices.txt72
-rw-r--r--doc/sample-config.toml11
3 files changed, 338 insertions, 0 deletions
diff --git a/doc/architecture.drawio b/doc/architecture.drawio
new file mode 100644
index 0000000..da8d093
--- /dev/null
+++ b/doc/architecture.drawio
@@ -0,0 +1,255 @@
+<mxfile host="Electron" modified="2025-07-21T16:24:00.333Z" 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="_IwkXMvgGTjRfqPQ7Xbc" version="22.1.2" type="device" pages="2">
+ <diagram name="Page-1" id="X3IhXaZUrfH3xSkZTgMi">
+ <mxGraphModel dx="1561" dy="962" 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="QjxI3e8E4HzF-40xZ6KW-25" value="Router daemon" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;" parent="1" vertex="1">
+ <mxGeometry x="690" y="40" width="280" height="290" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-37" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=classic;startFill=1;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-26" target="QjxI3e8E4HzF-40xZ6KW-34" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-26" value="RtMidi" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="920" y="130" width="50" height="110" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-27" target="QjxI3e8E4HzF-40xZ6KW-26" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-27" value="Deluge" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="1020" y="130" width="80" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-32" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;endArrow=none;endFill=0;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-28" target="QjxI3e8E4HzF-40xZ6KW-26" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-28" value="Akai MPK" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="1020" y="200" width="80" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-27" target="QjxI3e8E4HzF-40xZ6KW-27" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-34" value="Routing logic" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="810" y="155" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-38" value="Python GUI front-end" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;" parent="1" vertex="1">
+ <mxGeometry x="270" y="60" width="340" height="130" as="geometry" />
+ </mxCell>
+ <mxCell id="sy53--710sh8kRDpFuUL-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-42" target="QjxI3e8E4HzF-40xZ6KW-44" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-42" value="Protobuf" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="690" y="247.5" width="50" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-46" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-44" target="QjxI3e8E4HzF-40xZ6KW-34" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-44" value="Compiled mapping&lt;br&gt;(double buffered)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f5f5f5;fontColor=#333333;strokeColor=#666666;" parent="1" vertex="1">
+ <mxGeometry x="714" y="155" width="70" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-52" value="" 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="QjxI3e8E4HzF-40xZ6KW-47" target="QjxI3e8E4HzF-40xZ6KW-42" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <Array as="points">
+ <mxPoint x="670" y="125" />
+ <mxPoint x="670" y="273" />
+ </Array>
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-47" value="Protobuf" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="560" y="100" width="50" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-49" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-48" target="QjxI3e8E4HzF-40xZ6KW-34" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-51" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;startArrow=classic;startFill=1;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-48" target="QjxI3e8E4HzF-40xZ6KW-26" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="I2GwO2c-iuznto4u8GGd-1" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;startArrow=classic;startFill=1;endArrow=none;endFill=0;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-48" target="QjxI3e8E4HzF-40xZ6KW-44" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-48" value="Connection&lt;br&gt;manager" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="810" y="70" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-54" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="sy53--710sh8kRDpFuUL-3" target="QjxI3e8E4HzF-40xZ6KW-47" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-68" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;startArrow=classic;startFill=1;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-53" target="QjxI3e8E4HzF-40xZ6KW-66" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-53" value="NiceGUI&lt;br&gt;graph interface" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="340" y="95" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-59" 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="QjxI3e8E4HzF-40xZ6KW-55" target="QjxI3e8E4HzF-40xZ6KW-42" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-55" value="Python config loader" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;" parent="1" vertex="1">
+ <mxGeometry x="270" y="212.5" width="340" height="120" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-56" value="Protobuf" style="rounded=0;whiteSpace=wrap;html=1;dashed=1;" parent="1" vertex="1">
+ <mxGeometry x="560" y="252.5" width="50" height="50" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-57" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-39" target="QjxI3e8E4HzF-40xZ6KW-56" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-58" value="TOML loader" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="340" y="247.5" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-63" value="Filebrowser" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;" parent="1" vertex="1">
+ <mxGeometry x="60" y="60" width="160" height="120" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-64" value="Config file&lt;br&gt;(TOML)" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="80" y="95" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-70" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;startArrow=classic;startFill=1;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-65" target="QjxI3e8E4HzF-40xZ6KW-64" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-71" 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="QjxI3e8E4HzF-40xZ6KW-65" target="QjxI3e8E4HzF-40xZ6KW-67" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="205" y="320" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-65" value="Rest API" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="190" y="107.5" width="30" height="35" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-69" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;startArrow=classic;startFill=1;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-66" target="QjxI3e8E4HzF-40xZ6KW-65" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-66" value="Rest API" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="270" y="107.5" width="30" height="35" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-72" 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="QjxI3e8E4HzF-40xZ6KW-67" target="QjxI3e8E4HzF-40xZ6KW-58" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-67" value="Rest API" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="270" y="260" width="30" height="35" as="geometry" />
+ </mxCell>
+ <mxCell id="sy53--710sh8kRDpFuUL-2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-58" target="QjxI3e8E4HzF-40xZ6KW-39" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="410" y="277.5" as="sourcePoint" />
+ <mxPoint x="520" y="277.5" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="QjxI3e8E4HzF-40xZ6KW-39" value="Mapping compiler" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="450" y="247.5" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="sy53--710sh8kRDpFuUL-4" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="QjxI3e8E4HzF-40xZ6KW-53" target="sy53--710sh8kRDpFuUL-3" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="410" y="125" as="sourcePoint" />
+ <mxPoint x="560" y="125" as="targetPoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="sy53--710sh8kRDpFuUL-3" value="Mapping compiler" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="450" y="95" width="80" height="60" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+ <diagram id="eDi6a6V0f_dNDtGviYes" name="Page-2">
+ <mxGraphModel dx="1561" dy="946" 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="WbTO22Bo2XP_zg3Y817k-28" value="Main thread" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;dashed=1;align=left;verticalAlign=top;" parent="1" vertex="1">
+ <mxGeometry x="200" y="480" width="120" height="110" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-8" value="Thread" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;dashed=1;align=left;verticalAlign=top;" parent="1" vertex="1">
+ <mxGeometry x="70" y="150" width="120" height="210" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-5" value="polling" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WbTO22Bo2XP_zg3Y817k-1" target="WbTO22Bo2XP_zg3Y817k-4" edge="1">
+ <mxGeometry x="-0.5" relative="1" as="geometry">
+ <mxPoint as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-1" value="Deluge" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="90" y="60" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-7" value="function call" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WbTO22Bo2XP_zg3Y817k-4" target="WbTO22Bo2XP_zg3Y817k-6" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-4" value="File descriptor" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="90" y="180" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" parent="1" source="WbTO22Bo2XP_zg3Y817k-6" target="WbTO22Bo2XP_zg3Y817k-9" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-6" value="Callback" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="90" y="280" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WbTO22Bo2XP_zg3Y817k-9" target="WbTO22Bo2XP_zg3Y817k-25" edge="1">
+ <mxGeometry relative="1" as="geometry">
+ <mxPoint x="260" y="470" as="sourcePoint" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-9" value="Blocking concurrent queue" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="220" y="400" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-11" value="Thread" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;dashed=1;align=left;verticalAlign=top;" parent="1" vertex="1">
+ <mxGeometry x="200" y="150" width="120" height="210" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-12" value="polling" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WbTO22Bo2XP_zg3Y817k-13" target="WbTO22Bo2XP_zg3Y817k-15" edge="1">
+ <mxGeometry x="-0.5" relative="1" as="geometry">
+ <mxPoint as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-13" value="OP-1" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="220" y="60" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-14" value="function call" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WbTO22Bo2XP_zg3Y817k-15" target="WbTO22Bo2XP_zg3Y817k-16" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-15" value="File descriptor" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="220" y="180" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="WbTO22Bo2XP_zg3Y817k-16" target="WbTO22Bo2XP_zg3Y817k-9" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-16" value="Callback" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="220" y="280" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-17" value="Thread" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;dashed=1;align=left;verticalAlign=top;" parent="1" vertex="1">
+ <mxGeometry x="330" y="150" width="120" height="210" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-18" value="polling" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WbTO22Bo2XP_zg3Y817k-19" target="WbTO22Bo2XP_zg3Y817k-21" edge="1">
+ <mxGeometry x="-0.5" relative="1" as="geometry">
+ <mxPoint as="offset" />
+ </mxGeometry>
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-19" value="MPK" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="350" y="60" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-20" value="function call" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="WbTO22Bo2XP_zg3Y817k-21" target="WbTO22Bo2XP_zg3Y817k-22" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-21" value="File descriptor" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="350" y="180" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-24" 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="WbTO22Bo2XP_zg3Y817k-22" target="WbTO22Bo2XP_zg3Y817k-9" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-22" value="Callback" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="350" y="280" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-32" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;" parent="1" source="WbTO22Bo2XP_zg3Y817k-25" target="WbTO22Bo2XP_zg3Y817k-29" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="WbTO22Bo2XP_zg3Y817k-25" target="WbTO22Bo2XP_zg3Y817k-30" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;" parent="1" source="WbTO22Bo2XP_zg3Y817k-25" target="WbTO22Bo2XP_zg3Y817k-31" edge="1">
+ <mxGeometry relative="1" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-25" value="Router" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="220" y="510" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-29" value="Deluge" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="90" y="620" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-30" value="OP-1" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="220" y="620" width="80" height="60" as="geometry" />
+ </mxCell>
+ <mxCell id="WbTO22Bo2XP_zg3Y817k-31" value="MPK" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="350" y="620" width="80" height="60" as="geometry" />
+ </mxCell>
+ </root>
+ </mxGraphModel>
+ </diagram>
+</mxfile>
diff --git a/doc/devices.txt b/doc/devices.txt
new file mode 100644
index 0000000..7d3b323
--- /dev/null
+++ b/doc/devices.txt
@@ -0,0 +1,72 @@
+MPK
+
+- note_on, note_off
+ - keys
+ - pads
+
+- pitchwheel
+ - pitchwheel
+
+- control_change
+ - modulation wheel
+ - joystick
+ - play
+ - stop
+ - rec
+ - next/prev step
+ - knobs
+
+Deluge
+
+- note_on, note_off
+ - note input
+ - sequencer pads
+
+- start
+ - start button
+
+- stop
+ - start button
+
+- clock
+ - constantly during playback
+
+Xjam
+
+- note_on
+ - pads (never note_off though...)
+
+- aftertouch/polytouch
+ - pads, if enabled
+
+- control_change
+ - knobs
+
+- program_change
+ - configurable, currently nothing
+
+OP-1
+
+- note_on, note_off
+ - keys
+
+- continue
+ - start button (only with clock out/both)
+
+- stop
+ - stop button (only with clock out/both)
+
+- start
+ - start button but only from beginning of tape (only with clock out/both)
+
+- clock
+ - beat match: constantly sent, even if not playing
+
+- control_change (only in MIDI control mode)
+ - 4 knobs
+ - most non-keyboard keys
+
+- pitchwheel
+ - shift+left/right (only when it has the pitch effect also internally)
+
+
diff --git a/doc/sample-config.toml b/doc/sample-config.toml
new file mode 100644
index 0000000..fcd294a
--- /dev/null
+++ b/doc/sample-config.toml
@@ -0,0 +1,11 @@
+[devices]
+op1 = "OP-1 MIDI 1"
+deluge = "Deluge MIDI 1"
+mpk = "MPK mini Plus MIDI 1"
+xjam = "Xjam MIDI 1"
+
+[route.from.deluge.to]
+op1 = ["start", "stop", "continue"]
+
+[route.from.deluge.type]
+clock = ["all"]