aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--solo-tool-project/pyproject.toml3
-rw-r--r--solo-tool-project/src/solo_tool/external_volume.py31
-rw-r--r--web-project/src/solo_tool_web.py41
3 files changed, 65 insertions, 10 deletions
diff --git a/solo-tool-project/pyproject.toml b/solo-tool-project/pyproject.toml
index 40d09d8..2b70459 100644
--- a/solo-tool-project/pyproject.toml
+++ b/solo-tool-project/pyproject.toml
@@ -16,7 +16,8 @@ dependencies = [
"mido",
"python-mpv",
"pyaudio",
- "pydub-ng"
+ "pydub-ng",
+ "python-osc"
]
[project.optional-dependencies]
diff --git a/solo-tool-project/src/solo_tool/external_volume.py b/solo-tool-project/src/solo_tool/external_volume.py
new file mode 100644
index 0000000..425351a
--- /dev/null
+++ b/solo-tool-project/src/solo_tool/external_volume.py
@@ -0,0 +1,31 @@
+from pythonosc import udp_client
+
+class VolumeControllerXR18():
+ def __init__(self, ip: str):
+ port = 10024
+ self._client = udp_client.SimpleUDPClient(ip, port, timeout=1)
+ self._headphoneVolume = 0.0
+ self._speakerVolume = 0.0
+
+ def __del__(self):
+ self._client.close()
+
+ @property
+ def headphoneVolume(self) -> float:
+ return self._headphoneVolume
+
+ @headphoneVolume.setter
+ def headphoneVolume(self, new: float) -> None:
+ if new is not None:
+ self._headphoneVolume = min(max(new, 0.0), 1.0)
+ self._client.send_message("/config/solo/level", self._headphoneVolume)
+
+ @property
+ def speakerVolume(self) -> float:
+ return self._speakerVolume
+
+ @speakerVolume.setter
+ def speakerVolume(self, new: float) -> None:
+ if new is not None:
+ self._speakerVolume = min(max(new, 0.0), 1.0)
+ self._client.send_message("/lr/mix/fader", self._speakerVolume)
diff --git a/web-project/src/solo_tool_web.py b/web-project/src/solo_tool_web.py
index 4a3de3a..0315816 100644
--- a/web-project/src/solo_tool_web.py
+++ b/web-project/src/solo_tool_web.py
@@ -12,6 +12,7 @@ from solo_tool.session_manager import SessionManager
from solo_tool.midi_controller_actition import ActitionController
from solo_tool.recorder import Recorder
from solo_tool.metronome import Metronome
+from solo_tool.external_volume import VolumeControllerXR18
from recording import recordingControls
@@ -36,6 +37,7 @@ sessionManager = None
midiPedal = ActitionController()
recorder = None
metronome = None
+volumeController = None
def makeKeyboardHandler(st: SoloTool):
restartOrPrevious = handlers.restartOrPreviousSong(st, 0.01)
@@ -87,10 +89,18 @@ def sessionPage(sessionId: str):
with ui.header().classes('items-center justify-between'):
with ui.row().classes('items-center justify-start'):
ui.button(icon='menu', on_click=lambda: songDrawer.toggle()).props('flat dense round color=white')
- ui.label().bind_text_from(st, 'song', lambda index: fileName(st.songs[index]) if index is not None else "Select a song").classes('text-lg')
+ def songName(index):
+ if index is None:
+ return "Select a song"
+ else:
+ names = fileName(st.songs[index]).split(" - ")
+ return names[1] if len(names) > 1 else names[0]
+ ui.label().bind_text_from(st, 'song', songName).classes('text-lg')
+
with ui.row().classes('items-center justify-start'):
- buttonSize = "20px"
- bpm = ui.number(value=90, precision=0, min=10, max=300)
+ buttonSize = "18px"
+
+ bpm = ui.knob(value=90, min=10, max=300, step=1, show_value=True, color='white')#, track_color='white')
def toggleMetronome():
if metronome.running: metronome.stop()
else: metronome.start(bpm.value, 600)
@@ -98,10 +108,15 @@ def sessionPage(sessionId: str):
.props(f'flat dense round color=white size={buttonSize}') \
.bind_icon_from(metronome, "running", lambda running: "timer_off" if running else "timer")
- ui.button(icon='home', on_click=lambda: ui.navigate.to("/")).props(f'flat dense round color=white size={buttonSize}')
+ ui.button(icon='home', on_click=lambda: ui.navigate.to("/")) \
+ .props(f'flat dense round color=white size={buttonSize}')
+
def save(): sessionManager.saveSession(st, sessionId)
- ui.button(icon='save', on_click=save).props(f'flat dense round color=white size={buttonSize}')
- ui.button(icon='fullscreen', on_click=fullscreen.toggle).props(f'flat dense round color=white size={buttonSize}')
+ ui.button(icon='save', on_click=save) \
+ .props(f'flat dense round color=white size={buttonSize}')
+
+ ui.button(icon='fullscreen', on_click=fullscreen.toggle) \
+ .props(f'flat dense round color=white size={buttonSize}')
# Key points list
with ui.right_drawer(top_corner=True, bottom_corner=True).props('width=120 behavior=desktop'):
@@ -136,8 +151,15 @@ def sessionPage(sessionId: str):
# Volume slider
with ui.row().classes('w-full justify-between no-wrap items-center'):
- volumeLabels = ",".join([f"{v}:'{int(v*100)}%'" for v in [0.0, 0.25, 0.5, 0.75, 1.0, 1.25]])
- ui.slider(min=0, max=1.25, step=0.01).bind_value(st, 'volume').props(f':marker-labels="{{{volumeLabels}}}"').classes('q-px-md')
+ iconSize = "20px"
+ ui.icon('music_note').props(f"size={iconSize}").classes('q-px-xs')
+ ui.slider(min=0, max=1.25, step=0.01).classes('q-px-xs').bind_value(st, 'volume')
+
+ ui.icon('headphones').props(f"size={iconSize}").classes('q-px-xs')
+ ui.slider(value=0.0, min=0, max=1.0, step=0.01).classes('q-px-xs').bind_value(volumeController, 'headphoneVolume')
+
+ ui.icon('speaker').props(f"size={iconSize}").classes('q-px-xs')
+ ui.slider(value=0.0, min=0, max=1.0, step=0.01).classes('q-px-xs').bind_value(volumeController, 'speakerVolume')
# Playback rate and recording controls
with ui.row().classes('w-full justify-between no-wrap items-center'):
@@ -159,10 +181,11 @@ def landingPage():
ui.button(id, on_click=partial(ui.navigate.to, f"/{id}"))
def start(port, refresh, reload, sessionPath, bufferSize, samplingRate):
- global sessionManager, recorder, metronome
+ global sessionManager, recorder, metronome, volumeController
sessionManager = SessionManager(sessionPath)
recorder = Recorder(bufferSize, samplingRate)
metronome = Metronome(bufferSize, samplingRate)
+ volumeController = VolumeControllerXR18("XR18-7EE3D3.0xf7.com")
for id in sessionManager.getSessions():
songTool = sessionManager.loadSession(id)