Redesigned toolbar.
- Use a custom layout (instead of QToolBar). - Add a VUMeter for master output. - Move widget related stuff from player_state.py to project_view.pytime
parent
cb040f3686
commit
f8162bbb7e
|
@ -51,67 +51,6 @@ class MoveTo(enum.Enum):
|
|||
NextBeat = 3
|
||||
|
||||
|
||||
class TimeDisplayMode(enum.Enum):
|
||||
MusicalTime = 0
|
||||
RealTime = 1
|
||||
|
||||
|
||||
class TimeDisplay(QtWidgets.QLCDNumber):
|
||||
def __init__(self, parent: QtWidgets.QWidget, time_mapper: audioproc.TimeMapper) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.setDigitCount(9)
|
||||
self.setSegmentStyle(QtWidgets.QLCDNumber.Flat)
|
||||
self.setFrameStyle(QtWidgets.QFrame.Panel)
|
||||
self.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
|
||||
self.__time_mapper = time_mapper
|
||||
self.__time_mode = TimeDisplayMode.MusicalTime
|
||||
self.__current_time = audioproc.MusicalTime()
|
||||
|
||||
def __update(self) -> None:
|
||||
if self.__time_mode == TimeDisplayMode.MusicalTime:
|
||||
beat = self.__current_time / audioproc.MusicalDuration(1, 4)
|
||||
self.display('%.3f' % beat)
|
||||
|
||||
else:
|
||||
assert self.__time_mode == TimeDisplayMode.RealTime
|
||||
t = self.__time_mapper.musical_to_sample_time(self.__current_time) / self.__time_mapper.sample_rate
|
||||
millis = int(1000 * t) % 1000
|
||||
seconds = int(t) % 60
|
||||
minutes = int(t) // 60
|
||||
self.display('%d:%02d.%03d' % (minutes, seconds, millis))
|
||||
|
||||
def setCurrentTime(self, current_time: audioproc.MusicalTime) -> None:
|
||||
self.__current_time = current_time
|
||||
self.__update()
|
||||
|
||||
def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None:
|
||||
if evt.button() == Qt.LeftButton:
|
||||
if self.__time_mode == TimeDisplayMode.MusicalTime:
|
||||
self.__time_mode = TimeDisplayMode.RealTime
|
||||
else:
|
||||
self.__time_mode = TimeDisplayMode.MusicalTime
|
||||
self.__update()
|
||||
evt.accept()
|
||||
return
|
||||
|
||||
super().mousePressEvent(evt)
|
||||
|
||||
|
||||
class TimeDisplayAction(QtWidgets.QWidgetAction):
|
||||
def __init__(self, player_state: 'PlayerState', time_mapper: audioproc.TimeMapper) -> None:
|
||||
super().__init__(player_state)
|
||||
self.__player_state = player_state
|
||||
self.__time_mapper = time_mapper
|
||||
|
||||
def createWidget(self, parent: QtWidgets.QWidget) -> QtWidgets.QWidget:
|
||||
display = TimeDisplay(parent, self.__time_mapper)
|
||||
self.__player_state.currentTimeChanged.connect(display.setCurrentTime)
|
||||
display.setCurrentTime(self.__player_state.currentTime())
|
||||
return display
|
||||
|
||||
|
||||
class PlayerState(ui_base.ProjectMixin, QtCore.QObject):
|
||||
playingChanged = QtCore.pyqtSignal(bool)
|
||||
currentTimeChanged = QtCore.pyqtSignal(object)
|
||||
|
@ -135,8 +74,6 @@ class PlayerState(ui_base.ProjectMixin, QtCore.QObject):
|
|||
|
||||
self.__player_id = None # type: str
|
||||
|
||||
self.__time_display = TimeDisplayAction(self, self.time_mapper)
|
||||
|
||||
self.__move_to_start_action = QtWidgets.QAction("Move to start", self)
|
||||
self.__move_to_start_action.setIcon(QtGui.QIcon(
|
||||
os.path.join(constants.DATA_DIR, 'icons', 'media-skip-backward.svg')))
|
||||
|
@ -184,6 +121,24 @@ class PlayerState(ui_base.ProjectMixin, QtCore.QObject):
|
|||
def __set_session_value(self, key: str, value: Any) -> None:
|
||||
self.set_session_value(self.__session_prefix + key, value)
|
||||
|
||||
def togglePlaybackAction(self) -> QtWidgets.QAction:
|
||||
return self.__toggle_action
|
||||
|
||||
def toggleLoopAction(self) -> QtWidgets.QAction:
|
||||
return self.__loop_action
|
||||
|
||||
def moveToStartAction(self) -> QtWidgets.QAction:
|
||||
return self.__move_to_start_action
|
||||
|
||||
def moveToEndAction(self) -> QtWidgets.QAction:
|
||||
return self.__move_to_end_action
|
||||
|
||||
def moveToPrevAction(self) -> QtWidgets.QAction:
|
||||
return self.__move_to_prev_action
|
||||
|
||||
def moveToNextAction(self) -> QtWidgets.QAction:
|
||||
return self.__move_to_next_action
|
||||
|
||||
def playerID(self) -> str:
|
||||
return self.__player_id
|
||||
|
||||
|
@ -206,17 +161,6 @@ class PlayerState(ui_base.ProjectMixin, QtCore.QObject):
|
|||
if player_state.HasField('loop_end_time'):
|
||||
self.setLoopEndTime(audioproc.MusicalTime.from_proto(player_state.loop_end_time))
|
||||
|
||||
def populateToolBar(self, toolbar: QtWidgets.QToolBar) -> None:
|
||||
toolbar.addAction(self.__time_display)
|
||||
toolbar.addSeparator()
|
||||
toolbar.addAction(self.__toggle_action)
|
||||
toolbar.addAction(self.__loop_action)
|
||||
toolbar.addSeparator()
|
||||
toolbar.addAction(self.__move_to_start_action)
|
||||
toolbar.addAction(self.__move_to_prev_action)
|
||||
toolbar.addAction(self.__move_to_next_action)
|
||||
toolbar.addAction(self.__move_to_end_action)
|
||||
|
||||
def setTimeMode(self, mode: TimeMode) -> None:
|
||||
self.__time_mode = mode
|
||||
|
||||
|
@ -237,7 +181,8 @@ class PlayerState(ui_base.ProjectMixin, QtCore.QObject):
|
|||
def playing(self) -> bool:
|
||||
return self.__playing
|
||||
|
||||
def setCurrentTime(self, current_time: audioproc.MusicalTime, from_engine=False) -> None:
|
||||
def setCurrentTime(
|
||||
self, current_time: audioproc.MusicalTime, from_engine: bool = False) -> None:
|
||||
if current_time == self.__current_time:
|
||||
return
|
||||
|
||||
|
@ -321,14 +266,18 @@ class PlayerState(ui_base.ProjectMixin, QtCore.QObject):
|
|||
self.setCurrentTime(self.time_mapper.end_time)
|
||||
|
||||
elif where == MoveTo.PrevBeat:
|
||||
beat = int((self.__current_time + audioproc.MusicalDuration(3, 16)) / audioproc.MusicalTime(1, 4))
|
||||
beat = int(
|
||||
(self.__current_time + audioproc.MusicalDuration(3, 16))
|
||||
/ audioproc.MusicalTime(1, 4))
|
||||
new_time = audioproc.MusicalTime(beat - 1, 4)
|
||||
if new_time < audioproc.MusicalTime(0, 1):
|
||||
new_time = audioproc.MusicalTime(0, 1)
|
||||
self.setCurrentTime(new_time)
|
||||
|
||||
elif where == MoveTo.NextBeat:
|
||||
beat = int((self.__current_time + audioproc.MusicalDuration(3, 16)) / audioproc.MusicalTime(1, 4))
|
||||
beat = int(
|
||||
(self.__current_time + audioproc.MusicalDuration(3, 16))
|
||||
/ audioproc.MusicalTime(1, 4))
|
||||
new_time = audioproc.MusicalTime(beat + 1, 4)
|
||||
if new_time > self.time_mapper.end_time:
|
||||
new_time = self.time_mapper.end_time
|
||||
|
|
|
@ -20,11 +20,12 @@
|
|||
#
|
||||
# @end:license
|
||||
|
||||
import enum
|
||||
import functools
|
||||
import logging
|
||||
import uuid
|
||||
import typing
|
||||
from typing import Any, Tuple
|
||||
from typing import Any, Dict, Tuple
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5 import QtCore
|
||||
|
@ -39,6 +40,7 @@ from . import render_dialog
|
|||
from . import project_registry
|
||||
from .track_list import view as track_list_view
|
||||
from . import player_state as player_state_lib
|
||||
from . import vumeter
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from noisicaa import core
|
||||
|
@ -46,6 +48,55 @@ if typing.TYPE_CHECKING:
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TimeDisplayMode(enum.Enum):
|
||||
MusicalTime = 0
|
||||
RealTime = 1
|
||||
|
||||
|
||||
class TimeDisplay(QtWidgets.QLCDNumber):
|
||||
def __init__(self, parent: QtWidgets.QWidget, time_mapper: audioproc.TimeMapper) -> None:
|
||||
super().__init__(parent)
|
||||
|
||||
self.setDigitCount(9)
|
||||
self.setSegmentStyle(QtWidgets.QLCDNumber.Flat)
|
||||
self.setFrameStyle(QtWidgets.QFrame.Panel)
|
||||
self.setFrameShadow(QtWidgets.QFrame.Sunken)
|
||||
|
||||
self.__time_mapper = time_mapper
|
||||
self.__time_mode = TimeDisplayMode.MusicalTime
|
||||
self.__current_time = audioproc.MusicalTime()
|
||||
|
||||
def __update(self) -> None:
|
||||
if self.__time_mode == TimeDisplayMode.MusicalTime:
|
||||
beat = self.__current_time / audioproc.MusicalDuration(1, 4)
|
||||
self.display('%.3f' % beat)
|
||||
|
||||
else:
|
||||
assert self.__time_mode == TimeDisplayMode.RealTime
|
||||
t = (self.__time_mapper.musical_to_sample_time(self.__current_time)
|
||||
/ self.__time_mapper.sample_rate)
|
||||
millis = int(1000 * t) % 1000
|
||||
seconds = int(t) % 60
|
||||
minutes = int(t) // 60
|
||||
self.display('%d:%02d.%03d' % (minutes, seconds, millis))
|
||||
|
||||
def setCurrentTime(self, current_time: audioproc.MusicalTime) -> None:
|
||||
self.__current_time = current_time
|
||||
self.__update()
|
||||
|
||||
def mousePressEvent(self, evt: QtGui.QMouseEvent) -> None:
|
||||
if evt.button() == Qt.LeftButton:
|
||||
if self.__time_mode == TimeDisplayMode.MusicalTime:
|
||||
self.__time_mode = TimeDisplayMode.RealTime
|
||||
else:
|
||||
self.__time_mode = TimeDisplayMode.MusicalTime
|
||||
self.__update()
|
||||
evt.accept()
|
||||
return
|
||||
|
||||
super().mousePressEvent(evt)
|
||||
|
||||
|
||||
class ProjectView(ui_base.AbstractProjectView):
|
||||
playingChanged = QtCore.pyqtSignal(bool)
|
||||
loopEnabledChanged = QtCore.pyqtSignal(bool)
|
||||
|
@ -67,6 +118,8 @@ class ProjectView(ui_base.AbstractProjectView):
|
|||
self.__player_realm = None # type: str
|
||||
self.__player_node_id = None # type: str
|
||||
self.__player_status_listener = None # type: core.Listener
|
||||
self.__vumeter_node_id = None # type: str
|
||||
self.__vumeter_listener = None # type: core.Listener
|
||||
|
||||
self.__player_state = player_state_lib.PlayerState(context=self.context)
|
||||
|
||||
|
@ -86,14 +139,76 @@ class ProjectView(ui_base.AbstractProjectView):
|
|||
self.__splitter.setCollapsible(0, False)
|
||||
self.__splitter.addWidget(self.__graph)
|
||||
|
||||
self.__toolbar = QtWidgets.QToolBar(self)
|
||||
self.__toolbar.setObjectName('toolbar:%016x' % self.project.id)
|
||||
self.__player_state.populateToolBar(self.__toolbar)
|
||||
self.__time_display = TimeDisplay(self, self.time_mapper)
|
||||
self.__time_display.setMinimumWidth(9*20)
|
||||
self.__player_state.currentTimeChanged.connect(self.__time_display.setCurrentTime)
|
||||
self.__time_display.setCurrentTime(self.__player_state.currentTime())
|
||||
|
||||
self.__vumeter = vumeter.VUMeter(self)
|
||||
self.__vumeter.setMinimumWidth(250)
|
||||
|
||||
self.__toggle_playback_button = QtWidgets.QToolButton(self)
|
||||
self.__toggle_playback_button.setDefaultAction(self.__player_state.togglePlaybackAction())
|
||||
self.__toggle_playback_button.setIconSize(QtCore.QSize(54, 54))
|
||||
self.__toggle_playback_button.setAutoRaise(True)
|
||||
|
||||
self.__toggle_loop_button = QtWidgets.QToolButton(self)
|
||||
self.__toggle_loop_button.setDefaultAction(self.__player_state.toggleLoopAction())
|
||||
self.__toggle_loop_button.setIconSize(QtCore.QSize(24, 24))
|
||||
self.__toggle_loop_button.setAutoRaise(True)
|
||||
|
||||
self.__move_to_start_button = QtWidgets.QToolButton(self)
|
||||
self.__move_to_start_button.setDefaultAction(self.__player_state.moveToStartAction())
|
||||
self.__move_to_start_button.setIconSize(QtCore.QSize(24, 24))
|
||||
self.__move_to_start_button.setAutoRaise(True)
|
||||
|
||||
self.__move_to_end_button = QtWidgets.QToolButton(self)
|
||||
self.__move_to_end_button.setDefaultAction(self.__player_state.moveToEndAction())
|
||||
self.__move_to_end_button.setIconSize(QtCore.QSize(24, 24))
|
||||
self.__move_to_end_button.setAutoRaise(True)
|
||||
|
||||
self.__move_to_prev_button = QtWidgets.QToolButton(self)
|
||||
self.__move_to_prev_button.setDefaultAction(self.__player_state.moveToPrevAction())
|
||||
self.__move_to_prev_button.setIconSize(QtCore.QSize(24, 24))
|
||||
self.__move_to_prev_button.setAutoRaise(True)
|
||||
|
||||
self.__move_to_next_button = QtWidgets.QToolButton(self)
|
||||
self.__move_to_next_button.setDefaultAction(self.__player_state.moveToNextAction())
|
||||
self.__move_to_next_button.setIconSize(QtCore.QSize(24, 24))
|
||||
self.__move_to_next_button.setAutoRaise(True)
|
||||
|
||||
tb_layout = QtWidgets.QGridLayout()
|
||||
tb_layout.setContentsMargins(0, 2, 0, 2)
|
||||
tb_layout.setSpacing(0)
|
||||
c = 0
|
||||
tb_layout.addWidget(self.__toggle_playback_button, 0, c, 2, 1)
|
||||
c += 1
|
||||
tb_layout.addItem(QtWidgets.QSpacerItem(4, 4), 0, c, 2, 1)
|
||||
c += 1
|
||||
tb_layout.addWidget(self.__toggle_loop_button, 0, c, 1, 1)
|
||||
c += 1
|
||||
tb_layout.addItem(QtWidgets.QSpacerItem(4, 4), 0, c, 2, 1)
|
||||
c += 1
|
||||
tb_layout.addWidget(self.__move_to_start_button, 0, c, 1, 1)
|
||||
tb_layout.addWidget(self.__move_to_prev_button, 1, c, 1, 1)
|
||||
c += 1
|
||||
tb_layout.addWidget(self.__move_to_end_button, 0, c, 1, 1)
|
||||
tb_layout.addWidget(self.__move_to_next_button, 1, c, 1, 1)
|
||||
c += 1
|
||||
tb_layout.addItem(QtWidgets.QSpacerItem(4, 4), 0, c, 2, 1)
|
||||
c += 1
|
||||
tb_layout.addWidget(self.__time_display, 0, c, 2, 1)
|
||||
c += 1
|
||||
tb_layout.addItem(QtWidgets.QSpacerItem(4, 4), 0, c, 2, 1)
|
||||
c += 1
|
||||
tb_layout.addWidget(self.__vumeter, 0, c, 2, 1)
|
||||
c += 1
|
||||
tb_layout.setColumnStretch(c, 1)
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
layout.addWidget(self.__toolbar)
|
||||
layout.addLayout(tb_layout)
|
||||
layout.addWidget(self.__splitter)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
@ -115,7 +230,6 @@ class ProjectView(ui_base.AbstractProjectView):
|
|||
loop_end_time=self.__player_state.loopEndTimeProto()))
|
||||
|
||||
self.__player_node_id = uuid.uuid4().hex
|
||||
|
||||
await self.audioproc_client.add_node(
|
||||
'root',
|
||||
id=self.__player_node_id,
|
||||
|
@ -132,7 +246,40 @@ class ProjectView(ui_base.AbstractProjectView):
|
|||
'sink', 'in:right',
|
||||
node_db.PortDescription.AUDIO)
|
||||
|
||||
self.__vumeter_node_id = uuid.uuid4().hex
|
||||
await self.audioproc_client.add_node(
|
||||
'root',
|
||||
id=self.__vumeter_node_id,
|
||||
description=self.project.get_node_description('builtin://vumeter'))
|
||||
await self.audioproc_client.connect_ports(
|
||||
'root',
|
||||
self.__player_node_id, 'out:left',
|
||||
self.__vumeter_node_id, 'in:left',
|
||||
node_db.PortDescription.AUDIO)
|
||||
await self.audioproc_client.connect_ports(
|
||||
'root',
|
||||
self.__player_node_id, 'out:right',
|
||||
self.__vumeter_node_id, 'in:right',
|
||||
node_db.PortDescription.AUDIO)
|
||||
|
||||
self.__vumeter_listener = self.audioproc_client.node_messages.add(
|
||||
self.__vumeter_node_id, self.__vumeterMessage)
|
||||
|
||||
async def cleanup(self) -> None:
|
||||
if self.__vumeter_listener is not None:
|
||||
self.__vumeter_listener.remove()
|
||||
self.__vumeter_listener = None
|
||||
|
||||
if self.__vumeter_node_id is not None:
|
||||
assert self.__player_node_id is not None
|
||||
await self.audioproc_client.disconnect_ports(
|
||||
'root', self.__player_node_id, 'out:left', self.__vumeter_node_id, 'in:left')
|
||||
await self.audioproc_client.disconnect_ports(
|
||||
'root', self.__player_node_id, 'out:right', self.__vumeter_node_id, 'in:right')
|
||||
await self.audioproc_client.remove_node(
|
||||
'root', self.__vumeter_node_id)
|
||||
self.__vumeter_node_id = None
|
||||
|
||||
if self.__player_node_id is not None:
|
||||
await self.audioproc_client.disconnect_ports(
|
||||
'root', self.__player_node_id, 'out:left', 'sink', 'in:left')
|
||||
|
@ -167,6 +314,15 @@ class ProjectView(ui_base.AbstractProjectView):
|
|||
await self.audioproc_client.send_node_messages(
|
||||
self.__player_realm, audioproc.ProcessorMessageList(messages=[msg]))
|
||||
|
||||
def __vumeterMessage(self, msg: Dict[str, Any]) -> None:
|
||||
meter = 'http://noisicaa.odahoda.de/lv2/processor_vumeter#meter'
|
||||
if meter in msg:
|
||||
current_left, peak_left, current_right, peak_right = msg[meter]
|
||||
self.__vumeter.setLeftValue(current_left)
|
||||
self.__vumeter.setLeftPeak(peak_left)
|
||||
self.__vumeter.setRightValue(current_right)
|
||||
self.__vumeter.setRightPeak(peak_right)
|
||||
|
||||
def onRender(self) -> None:
|
||||
dialog = render_dialog.RenderDialog(parent=self, context=self.context)
|
||||
dialog.setModal(True)
|
||||
|
|
Loading…
Reference in New Issue