aboutsummaryrefslogtreecommitdiffstats
path: root/web-project
diff options
context:
space:
mode:
Diffstat (limited to 'web-project')
-rw-r--r--web-project/src/recording.py111
-rw-r--r--web-project/src/solo_tool_web.py18
2 files changed, 123 insertions, 6 deletions
diff --git a/web-project/src/recording.py b/web-project/src/recording.py
new file mode 100644
index 0000000..2afd567
--- /dev/null
+++ b/web-project/src/recording.py
@@ -0,0 +1,111 @@
+from pathlib import Path
+from contextlib import contextmanager
+from asyncio import sleep
+from tempfile import TemporaryDirectory
+from datetime import date
+
+from nicegui import ui, run
+
+_recording = None
+
+@contextmanager
+def _disable(button: ui.button):
+ button.disable()
+ try:
+ yield
+ finally:
+ button.enable()
+
+async def _stopRecording(recordButton, uploadButton, recorder, wavFile):
+ with _disable(recordButton):
+ global _recording
+ _recording = recorder.stopRecording()
+ await run.cpu_bound(_recording.writeWav, wavFile)
+ uploadButton.enable()
+
+def _makeRecordCallback(playButton, recordButton, uploadButton, soloTool, recorder, wavFile):
+ async def f():
+ if recorder.recording:
+ await _stopRecording(recordButton, uploadButton, recorder, wavFile)
+ else:
+ if soloTool.playingAdHoc:
+ soloTool.backToNormal()
+ uploadButton.disable()
+ recorder.startRecording()
+ playButton.enable()
+ return f
+
+def _makePlayCallback(playButton, recordButton, uploadButton, soloTool, recorder, wavFile):
+ async def f():
+ with _disable(playButton):
+ if recorder.recording:
+ await _stopRecording(recordButton, uploadButton, recorder, wavFile)
+
+ if soloTool.playingAdHoc:
+ soloTool.backToNormal()
+ else:
+ soloTool.playAdHoc(wavFile)
+ soloTool.play()
+ return f
+
+def _makeUploadCallback(playButton, recordButton, uploadButton, tempDir, sessionManager):
+ async def f():
+ with ui.dialog() as dialog, ui.card():
+ fileName = ui.input(label='File name', value="jam.mp3")
+ with ui.row():
+ ui.button('Upload', color='positive', on_click=lambda: dialog.submit(fileName.value))
+ ui.button('Cancel', color='negative' ,on_click=lambda: dialog.submit(None))
+
+ fileName = await dialog
+ if fileName is None:
+ return
+
+ playButton.disable()
+ recordButton.disable()
+ uploadButton.disable()
+
+ def on_dismiss():
+ playButton.enable()
+ recordButton.enable()
+ uploadButton.enable()
+ n = ui.notification(timeout=None, position='bottom-right', type='ongoing', spinner=True, on_dismiss=on_dismiss, icon='check')
+
+ n.message = f'Converting to .mp3...'
+ mp3File = Path(tempDir.name) / fileName
+ await run.cpu_bound(_recording.writeMp3, mp3File)
+
+ n.message = 'Uploading...'
+ folderName = date.today().isoformat()
+ try:
+ await run.io_bound(sessionManager.saveRecording, mp3File, f"{folderName}/{fileName}")
+ except:
+ n.spinner = False
+ n.icon = 'error'
+ n.message = 'Upload failed!'
+ n.close_button = 'Close'
+ return
+
+ n.spinner = False
+ n.message = 'Done!'
+ await sleep(2)
+ n.dismiss()
+ return f
+
+def recordingControls(soloTool, recorder, sessionManager):
+ tempDir = TemporaryDirectory(prefix="solotool-")
+ wavFile = Path(tempDir.name) / "st_recording.wav"
+
+ with ui.button_group().classes('').style('height: 40px'):
+ recordButton = ui.button(icon='fiber_manual_record', color='negative') \
+ .bind_icon_from(recorder, 'recording', lambda recording: 'radio_button_unchecked' if recording else 'fiber_manual_record')
+
+ playButton = ui.button(icon='hearing') \
+ .bind_icon_from(soloTool, 'playingAdHoc', lambda adHoc: 'close' if adHoc else 'hearing')
+ playButton.disable()
+
+ uploadButton = ui.button(icon='cloud_upload')
+ uploadButton.disable()
+
+ recordButton.on_click(_makeRecordCallback(playButton, recordButton, uploadButton, soloTool, recorder, wavFile))
+ playButton.on_click(_makePlayCallback(playButton, recordButton, uploadButton, soloTool, recorder, wavFile))
+ uploadButton.on_click(_makeUploadCallback(playButton, recordButton, uploadButton, tempDir, sessionManager))
diff --git a/web-project/src/solo_tool_web.py b/web-project/src/solo_tool_web.py
index 3c68585..02ffdac 100644
--- a/web-project/src/solo_tool_web.py
+++ b/web-project/src/solo_tool_web.py
@@ -10,6 +10,9 @@ from urllib.parse import unquote
from solo_tool import SoloTool, handlers
from solo_tool.session_manager import SessionManager
from solo_tool.midi_controller_actition import ActitionController
+from solo_tool.recorder import Recorder
+
+from recording import recordingControls
def fileName(path: str) -> str:
return unquote(basename(splitext(path)[0]))
@@ -30,6 +33,7 @@ def songList(st: SoloTool, songDrawer) -> None:
sessions = {}
sessionManager = None
midiPedal = ActitionController()
+recorder = Recorder(1024, 48000)
def makeKeyboardHandler(st: SoloTool):
def handleKey(e: events.KeyEventArguments):
@@ -103,15 +107,17 @@ def sessionPage(sessionId: str):
ui.button(icon='undo', on_click=st.jump, color='secondary').props(f"size={buttonSize}").style('flex: 2')
ui.button(icon='skip_next', on_click=handlers.songRelative(st, 1)).props(f"size={buttonSize}").style('flex: 1')
- # Playback rate
+ # 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')
+
+ # Playback rate and recording controls
with ui.row().classes('w-full justify-between no-wrap items-center'):
markerLabels = ",".join([f"{v}:'{v}x'" for v in [0.4, 0.6, 0.8, 1.0, 1.2]])
ui.slider(min=0.4, max=1.2, step=0.05).bind_value(st, 'rate').props(f'snap markers :marker-labels="{{{markerLabels}}}"').classes('q-px-md')
- # Volume
- 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')
+ recordingControls(st, recorder, sessionManager)
@ui.page('/')
def landingPage():
@@ -151,4 +157,4 @@ def main(port, refresh, reload, session_path):
# Hardcoded dev settings
if __name__ in {"__main__", "__mp_main__"}:
start(8080, 0.5, False, "https://files.0xf7.com")
- #start(8080, 0.5, True, "/home/eddy/music/sessions")
+ #start(8080, 0.5, True, "/home/eddy/music")