aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/diagram.drawio48
-rw-r--r--solo-tool-project/src/solo_tool/handlers.py7
-rw-r--r--web-project/src/solo_tool_web.py87
3 files changed, 75 insertions, 67 deletions
diff --git a/doc/diagram.drawio b/doc/diagram.drawio
index bb8dbd0..424570a 100644
--- a/doc/diagram.drawio
+++ b/doc/diagram.drawio
@@ -1,6 +1,6 @@
-<mxfile host="Electron" modified="2025-02-23T16:24:22.548Z" 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="8sa4v4RLC4vbyHn2WtNr" version="22.1.2" type="device" pages="2">
+<mxfile host="Electron" modified="2025-02-24T11:00:29.036Z" 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="9_vGhwK_Mq_I_v5YUap0" version="22.1.2" type="device" pages="2">
<diagram id="PyNSc7ezSt42GBdjTBvd" name="Launchpad">
- <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">
+ <mxGraphModel dx="1562" dy="963" 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="ZtjfeE3uwfRsFhnWfLYL-0" />
<mxCell id="ZtjfeE3uwfRsFhnWfLYL-1" parent="ZtjfeE3uwfRsFhnWfLYL-0" />
@@ -200,51 +200,51 @@
</mxGraphModel>
</diagram>
<diagram id="R-0UAU87gWX4lK6NCzNs" name="Web wireframe">
- <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">
+ <mxGraphModel dx="1302" dy="803" 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="0goJ5iq8U8227kam6OUo-1" value="Song list" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="80" y="80" width="240" height="360" as="geometry" />
+ <mxCell id="0goJ5iq8U8227kam6OUo-1" value="Key point list" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="160" y="80" width="80" height="240" as="geometry" />
</mxCell>
<mxCell id="0goJ5iq8U8227kam6OUo-2" value="Volume slider" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="320" y="80" width="440" height="40" as="geometry" />
+ <mxGeometry x="380" y="280" width="260" height="40" as="geometry" />
</mxCell>
- <mxCell id="0goJ5iq8U8227kam6OUo-3" value="Speed slider" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="360" y="160" width="360" height="40" as="geometry" />
+ <mxCell id="0goJ5iq8U8227kam6OUo-3" value="Speed" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="280" y="280" width="60" height="40" as="geometry" />
</mxCell>
<mxCell id="0goJ5iq8U8227kam6OUo-4" value="Speed +5" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="720" y="160" width="40" height="40" as="geometry" />
+ <mxGeometry x="340" y="280" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="0goJ5iq8U8227kam6OUo-5" value="Speed -5" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="320" y="160" width="40" height="40" as="geometry" />
+ <mxGeometry x="240" y="280" width="40" height="40" as="geometry" />
</mxCell>
<mxCell id="E9TFIlIWBKXALOEyTYeL-1" value="Seek slider" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="390" y="240" width="300" height="40" as="geometry" />
+ <mxGeometry x="240" y="120" width="440" height="40" as="geometry" />
</mxCell>
<mxCell id="E9TFIlIWBKXALOEyTYeL-2" value="Prev song" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="320" y="240" width="70" height="40" as="geometry" />
+ <mxGeometry x="240" y="200" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="e6dzTLVl2QyovQL1D1hT-10" value="Next song" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="690" y="240" width="70" height="40" as="geometry" />
+ <mxGeometry x="600" y="200" width="80" height="80" as="geometry" />
</mxCell>
- <mxCell id="2VOf0fCjGpZdvwMfuWx9-2" value="Set key point" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="440" y="320" width="200" height="40" as="geometry" />
- </mxCell>
- <mxCell id="eBDQebIGrGTMiAxLC66R-1" value="Previous" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="320" y="320" width="120" height="40" as="geometry" />
- </mxCell>
- <mxCell id="eBDQebIGrGTMiAxLC66R-2" value="Next" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="640" y="320" width="120" height="40" as="geometry" />
+ <mxCell id="2VOf0fCjGpZdvwMfuWx9-2" value="Key point slider" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
+ <mxGeometry x="240" y="160" width="440" height="40" as="geometry" />
</mxCell>
<mxCell id="e5je7AeTKV-z7aj2oazw-2" value="Stop" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="320" y="400" width="120" height="40" as="geometry" />
+ <mxGeometry x="320" y="200" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="e5je7AeTKV-z7aj2oazw-3" value="Play/pause" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="600" y="400" width="160" height="40" as="geometry" />
+ <mxGeometry x="400" y="200" width="100" height="80" as="geometry" />
</mxCell>
<mxCell id="e5je7AeTKV-z7aj2oazw-4" value="Jump" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
- <mxGeometry x="440" y="400" width="160" height="40" as="geometry" />
+ <mxGeometry x="500" y="200" width="100" height="80" as="geometry" />
+ </mxCell>
+ <mxCell id="ZINFS9bsx5oSfdTS2e79-1" value="Full&lt;br&gt;screen" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
+ <mxGeometry x="640" y="280" width="40" height="40" as="geometry" />
+ </mxCell>
+ <mxCell id="ZINFS9bsx5oSfdTS2e79-4" value="Song name" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
+ <mxGeometry x="240" y="80" width="440" height="40" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
diff --git a/solo-tool-project/src/solo_tool/handlers.py b/solo-tool-project/src/solo_tool/handlers.py
index 1e0e22c..975ce8d 100644
--- a/solo-tool-project/src/solo_tool/handlers.py
+++ b/solo-tool-project/src/solo_tool/handlers.py
@@ -18,6 +18,13 @@ def changeSong(st: SoloTool, delta: int) -> Callable[[], None]:
st.song += delta
return f
+def setSong(st: SoloTool, index: int, followUp: Callable[[], None]=None) -> Callable[[], None]:
+ def f():
+ st.song = index
+ if followUp is not None:
+ followUp()
+ return f
+
def seekRelative(st: SoloTool, delta: float) -> Callable[[], None]:
def f():
st.position += delta
diff --git a/web-project/src/solo_tool_web.py b/web-project/src/solo_tool_web.py
index edca008..d18ae96 100644
--- a/web-project/src/solo_tool_web.py
+++ b/web-project/src/solo_tool_web.py
@@ -7,55 +7,56 @@ from solo_tool.midi_controller_launchpad_mini import MidiController
st = loadSession("/home/eddy/music/funk-band/practice.json")
midiController = MidiController(st)
-midiController.connect()
+#midiController.connect()
+
+def songName(path):
+ from os.path import basename, splitext
+ return basename(splitext(path)[0])
def main():
fullscreen = ui.fullscreen()
ui.dark_mode().enable()
ui.colors(secondary='#ffc107')
- with ui.row() as mainContainer:
- mainContainer.style('width: 100%; height: 100%;')
-
- with ui.column() as keyPointColumn:
- keyPointColumn.style('width: 12%; height: 100%;')
-
- ui.label('Key Points')
-
- with ui.scroll_area():
- for i, song in enumerate(st.songs):
- with ui.list().props('dense separator').bind_visibility_from(st, 'song', value=i) as keyPointList:
- for kp in st._keyPoints[i]:
- ui.item(f"{kp}", on_click=handlers.setKeyPoint(st, kp)).props('clickable')
-
- with ui.column() as controlColumn:
- controlColumn.style('width: 84%; height: 100%;')
-
- ui.label("Song name").bind_text_from(st, 'song', lambda index: st.songs[index] if index is not None else "")
-
- # Playback position
- ui.slider(min=0, max=1.0, step=0.01).bind_value(st, 'position').props('thumb-size=0px track-size=16px')
-
- # Key point position
- ui.slider(min=0, max=1.0, step=0.01).bind_value(st, 'keyPoint').props('color=secondary')
-
- # Play control
- with ui.button_group().classes('w-full').style('height: 90px'):
- ui.button(icon='skip_previous', on_click=handlers.changeSong(st, -1)).style('flex: 1')
- ui.button(icon='stop', on_click=st.stop, color='negative').style('flex: 1')
- ui.button(color='positive', on_click=handlers.playPause(st)).bind_icon_from(st, "playing", lambda playing: "pause" if playing else "play_arrow").style('flex: 2')
- ui.button(icon='undo', on_click=st.jump, color='secondary').style('flex: 2')
- ui.button(icon='skip_next', on_click=handlers.changeSong(st, 1)).style('flex: 1')
-
- # Volume and rate
- with ui.row().classes('w-full justify-between no-wrap'):
- ui.button(icon='fast_rewind', on_click=handlers.rateRelative(st, -0.05))
- ui.label().bind_text_from(st, 'rate', lambda rate: f'{rate:0.3}').on('click', handlers.rateAbsolute(st, 1.0))
- ui.button(icon='fast_forward', on_click=handlers.rateRelative(st, 0.05))
- ui.slider(min=0, max=1.2, step=0.01).bind_value(st, 'volume')
- ui.button(icon='fullscreen', on_click=fullscreen.toggle).props('outline')
-
- ui.run()
+ with ui.header().classes('items-center'):
+ ui.button(icon='menu', on_click=lambda: song_drawer.toggle()).props('flat round dense color=white')
+ ui.label().bind_text_from(st, 'song', lambda index: songName(st.songs[index]) if index is not None else "Select a song").classes('text-lg')
+
+ with ui.right_drawer(top_corner=True, bottom_corner=True).props('width=120 behavior=desktop'):
+ ui.label("Key Points").classes('text-lg')
+ for i, song in enumerate(st.songs):
+ with ui.list().props('separator').bind_visibility_from(st, 'song', value=i):
+ for kp in st._keyPoints[i]:
+ ui.item(f"{kp:0.2}", on_click=handlers.setKeyPoint(st, kp)).props('clickable v-ripple')
+
+ with ui.left_drawer(bottom_corner=True).props('overlay') as song_drawer:
+ with ui.list().props('separator'):
+ for i, path in enumerate(st.songs):
+ ui.item(songName(path), on_click=handlers.setSong(st, i, lambda: song_drawer.hide())).props('clickable v-ripple')
+
+ # Playback position
+ ui.slider(min=0, max=1.0, step=0.01).bind_value(st, 'position').props('thumb-size=0px track-size=16px')
+
+ # Key point position
+ ui.slider(min=0, max=1.0, step=0.01).bind_value(st, 'keyPoint').props('color=secondary')
+
+ # Play control
+ with ui.button_group().classes('w-full').style('height: 90px'):
+ ui.button(icon='skip_previous', on_click=handlers.changeSong(st, -1)).style('flex: 1')
+ ui.button(icon='stop', on_click=st.stop, color='negative').style('flex: 1')
+ ui.button(color='positive', on_click=handlers.playPause(st)).bind_icon_from(st, "playing", lambda playing: "pause" if playing else "play_arrow").style('flex: 2')
+ ui.button(icon='undo', on_click=st.jump, color='secondary').style('flex: 2')
+ ui.button(icon='skip_next', on_click=handlers.changeSong(st, 1)).style('flex: 1')
+
+ # Volume and rate
+ with ui.row().classes('w-full justify-between no-wrap items-center'):
+ ui.button(icon='fast_rewind', on_click=handlers.rateRelative(st, -0.05))
+ ui.label().bind_text_from(st, 'rate', lambda rate: f'{rate:0.3}x').on('click', handlers.rateAbsolute(st, 1.0)).classes('text-lg')
+ ui.button(icon='fast_forward', on_click=handlers.rateRelative(st, 0.05))
+ ui.slider(min=0, max=1.2, step=0.01).bind_value(st, 'volume')
+ ui.button(icon='fullscreen', on_click=fullscreen.toggle).props('outline')
+
+ ui.run()
if __name__ in {'__main__', '__mp_main__'}:
main()