Select audio backend in settings dialog.

looper
Ben Niemann 7 years ago
parent e90d05f240
commit 9343800820

@ -63,6 +63,10 @@ class AudioProcClientMixin(object):
'DISCONNECT_PORTS', self._session_id,
node1_id, port1_name, node2_id, port2_name)
async def set_backend(self, name, **args):
return await self._stub.call(
'SET_BACKEND', self._session_id, name, args)
def handle_pipeline_mutation(self, mutation):
logger.info("Mutation received: %s" % mutation)

@ -68,6 +68,10 @@ class Session(object):
class AudioProcProcessMixin(object):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.backend = None
async def setup(self):
await super().setup()
@ -88,6 +92,8 @@ class AudioProcProcessMixin(object):
'CONNECT_PORTS', self.handle_connect_ports)
self.server.add_command_handler(
'DISCONNECT_PORTS', self.handle_disconnect_ports)
self.server.add_command_handler(
'SET_BACKEND', self.handle_set_backend)
self.node_db = node_db.NodeDB()
self.node_db.add(scale.Scale)
@ -99,8 +105,7 @@ class AudioProcProcessMixin(object):
self.pipeline = pipeline.Pipeline()
self.pipeline.utilization_callback = self.utilization_callback
self.backend = backend.NullBackend()
self.pipeline.set_backend(self.backend)
self.backend = None
self.audiosink = backend.AudioSinkNode()
self.audiosink.setup()
@ -223,6 +228,20 @@ class AudioProcProcessMixin(object):
mutations.DisconnectPorts(
node1.outputs[port1_name], node2.inputs[port2_name]))
def handle_set_backend(self, session_id, name, args):
self.get_session(session_id)
if name == 'pyaudio':
be = backend.PyAudioBackend(**args)
elif name == 'null':
be = backend.NullBackend(**args)
elif name is None:
be = None
else:
raise ValueError("Invalid backend name %s" % name)
self.pipeline.set_backend(be)
class AudioProcProcess(AudioProcProcessMixin, core.ProcessImpl):
pass

@ -26,7 +26,7 @@ class AudioSinkNode(Node):
desc.is_system = True
def __init__(self):
super().__init__()
super().__init__(id='sink')
self._input = AudioInputPort('in')
self.add_input(self._input)

@ -12,8 +12,8 @@ logger = logging.getLogger(__name__)
class Node(object):
desc = None
def __init__(self, name=None):
self.id = uuid.uuid4().hex
def __init__(self, name=None, id=None):
self.id = id or uuid.uuid4().hex
self.pipeline = None
self._name = name or type(self).__name__
self.inputs = {}

@ -102,14 +102,15 @@ class Pipeline(object):
def mainloop(self):
try:
logger.info("Setting up backend...")
self._backend.setup()
logger.info("Starting mainloop...")
self._started.set()
timepos = 0
while not self._stopping.is_set():
with self.reader_lock():
if self._backend is None:
time.sleep(0.1)
continue
t0 = time.time()
self._backend.wait()
@ -136,9 +137,6 @@ class Pipeline(object):
for node in reversed(self.sorted_nodes):
node.cleanup()
logger.info("Cleaning up backend...")
self._backend.cleanup()
@property
def sorted_nodes(self):
graph = dict((node, set(node.parent_nodes))
@ -167,7 +165,18 @@ class Pipeline(object):
self._nodes.remove(node)
def set_backend(self, backend):
self._backend = backend
with self.writer_lock():
if self._backend is not None:
logger.info(
"Clean up backend %s", type(self._backend).__name__)
self._backend.cleanup()
self._backend = None
if backend is not None:
logger.info(
"Set up backend %s", type(backend).__name__)
backend.setup()
self._backend = backend
@property
def backend(self):

@ -202,7 +202,6 @@ class ObjectPropertyBase(PropertyBase):
class ObjectProperty(ObjectPropertyBase):
def __set__(self, instance, value):
logger.info("%s.%s=%s", type(instance), self.name, value)
if value is not None and not isinstance(value, self.cls):
raise TypeError(
"Expected %s, got %s" % (

@ -129,6 +129,9 @@ class BaseEditorApp(QApplication):
await self.audioproc_client.setup()
await self.audioproc_client.connect(self.audioproc_process)
await self.audioproc_client.set_backend(
self.settings.value('audio/backend', 'pyaudio'))
async def cleanup(self):
logger.info("Cleaning up.")

@ -4,44 +4,36 @@
# message "Access to a protected member .. of a client class"
# pylint: disable=W0212
import functools
import os.path
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import (
QComboBox,
QDialog,
QHBoxLayout,
QLabel,
QPushButton,
QStyleFactory,
QTabWidget,
QVBoxLayout,
QWidget,
)
from PyQt5 import QtGui
from PyQt5 import QtWidgets
from ..constants import DATA_DIR
from . import ui_base
class SettingsDialog(ui_base.CommonMixin, QDialog):
class SettingsDialog(ui_base.CommonMixin, QtWidgets.QDialog):
def __init__(self, app, parent):
super().__init__(app=app, parent=parent)
self.setWindowTitle("noisicaä - Settings")
self.resize(600, 300)
self.tabs = QTabWidget(self)
self.tabs = QtWidgets.QTabWidget(self)
for cls in (AppearancePage, AudioPage):
page = cls(self.app)
self.tabs.addTab(page, page.getIcon(), page.title)
close = QPushButton("Close")
close = QtWidgets.QPushButton("Close")
close.clicked.connect(self.close)
buttons = QHBoxLayout()
buttons = QtWidgets.QHBoxLayout()
buttons.addStretch(1)
buttons.addWidget(close)
layout = QVBoxLayout()
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.tabs, 1)
layout.addLayout(buttons)
@ -65,11 +57,11 @@ class SettingsDialog(ui_base.CommonMixin, QDialog):
s.endGroup()
class Page(ui_base.CommonMixin, QWidget):
class Page(ui_base.CommonMixin, QtWidgets.QWidget):
def __init__(self, app):
super().__init__(app=app)
layout = QVBoxLayout()
layout = QtWidgets.QVBoxLayout()
self.createOptions(layout)
@ -80,25 +72,25 @@ class Page(ui_base.CommonMixin, QWidget):
class AppearancePage(Page):
def __init__(self, app):
self.title = "Appearance"
self._qt_styles = sorted(QStyleFactory.keys())
self._qt_styles = sorted(QtWidgets.QStyleFactory.keys())
super().__init__(app)
def getIcon(self):
path = os.path.join(DATA_DIR, 'icons', 'settings_appearance.png')
return QIcon(path)
return QtGui.QIcon(path)
def createOptions(self, layout):
self.createQtStyle(layout)
def createQtStyle(self, parent):
layout = QHBoxLayout()
layout = QtWidgets.QHBoxLayout()
parent.addLayout(layout)
label = QLabel("Qt Style:")
label = QtWidgets.QLabel("Qt Style:")
layout.addWidget(label)
combo = QComboBox()
combo = QtWidgets.QComboBox()
layout.addWidget(combo)
current = self.app.settings.value(
@ -113,7 +105,7 @@ class AppearancePage(Page):
def qtStyleChanged(self, index):
style_name = self._qt_styles[index]
style = QStyleFactory.create(style_name)
style = QtWidgets.QStyleFactory.create(style_name)
self.app.setStyle(style)
self.app.settings.setValue('appearance/qtStyle', style_name)
@ -122,11 +114,57 @@ class AppearancePage(Page):
class AudioPage(Page):
def __init__(self, app):
self.title = "Audio"
self._backends = ['pyaudio', 'null']
super().__init__(app)
def getIcon(self):
path = os.path.join(DATA_DIR, 'icons', 'settings_audio.png')
return QIcon(path)
return QtGui.QIcon(path)
def createOptions(self, layout):
pass
backend_layout = QtWidgets.QHBoxLayout()
layout.addLayout(backend_layout)
label = QtWidgets.QLabel("Backend:")
backend_layout.addWidget(label)
combo = QtWidgets.QComboBox()
backend_layout.addWidget(combo, stretch=1)
current = self.app.settings.value('audio/backend', 'pyaudio')
for index, backend in enumerate(self._backends):
combo.addItem(backend)
if backend == current:
combo.setCurrentIndex(index)
combo.currentIndexChanged.connect(self.backendChanged)
layout.addStretch()
buttons_layout = QtWidgets.QHBoxLayout()
layout.addLayout(buttons_layout)
buttons_layout.addStretch()
test_button = QtWidgets.QPushButton("Test")
buttons_layout.addWidget(test_button)
test_button.clicked.connect(self.testBackend)
def backendChanged(self, index):
backend = self._backends[index]
self.call_async(
self.app.audioproc_client.set_backend(backend),
callback=functools.partial(
self._set_backend_done, backend=backend))
def _set_backend_done(self, result, backend):
self.app.settings.setValue('audio/backend', backend)
def testBackend(self):
self.call_async(self._testBackendAsync())
async def _testBackendAsync(self):
node = await self.app.audioproc_client.add_node(
'wavfile', path='/usr/share/sounds/purple/send.wav')
await self.app.audioproc_client.connect_ports(
node, 'out', 'sink', 'in')

Loading…
Cancel
Save