From ceee5613c75ccb843e6aa14f7983a260aa40c8ac Mon Sep 17 00:00:00 2001 From: root Date: Fri, 3 Jul 2020 04:49:30 +0100 Subject: [PATCH 1/2] Basic engine for VCV Rack --- zyngine/__init__.py | 4 +- zyngine/zynthian_engine_vcv_rack.py | 137 ++++++++++++++++++++++++++++ zyngui/zynthian_gui_engine.py | 4 + zyngui/zynthian_gui_layer.py | 3 + 4 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 zyngine/zynthian_engine_vcv_rack.py diff --git a/zyngine/__init__.py b/zyngine/__init__.py index c292bfc90..a1e0c4207 100644 --- a/zyngine/__init__.py +++ b/zyngine/__init__.py @@ -17,7 +17,8 @@ "zynthian_engine_jalv", "zynthian_engine_csound", "zynthian_engine_mixer", - "zynthian_engine_transport" + "zynthian_engine_transport", + "zynthian_engine_vcv_rack" ] #from zyngine.zynthian_midi import * from zyngine.zynthian_zcmidi import * @@ -38,3 +39,4 @@ from zyngine.zynthian_engine_csound import * from zyngine.zynthian_engine_mixer import * from zyngine.zynthian_engine_transport import * +from zyngine.zynthian_engine_vcv_rack import * diff --git a/zyngine/zynthian_engine_vcv_rack.py b/zyngine/zynthian_engine_vcv_rack.py new file mode 100644 index 000000000..2e2439ef8 --- /dev/null +++ b/zyngine/zynthian_engine_vcv_rack.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +#****************************************************************************** +# ZYNTHIAN PROJECT: Zynthian Engine (zynthian_engine_vcv_rack) +# +# zynthian_engine implementation for VCV Rack Synthesizer +# +#****************************************************************************** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# For a full copy of the GNU General Public License see the LICENSE.txt file. +# +#****************************************************************************** +import os +import json +import logging +import subprocess + +from . import zynthian_engine + +def check_vcv_rack_binary(): + if os.path.isfile(VCV_RACK_BINARY) and os.access(VCV_RACK_BINARY, os.X_OK): + return True + else: + return False + +VCV_RACK_DIR = "{}/vcvrack.raspbian-v1".format(os.environ.get('ZYNTHIAN_SW_DIR', "/zynthian/zynthian-sw")) +VCV_RACK_BINARY = "{}/Rack".format(VCV_RACK_DIR) +VCV_RACK_CONFIG = "{}/settings.json".format(VCV_RACK_DIR) + +VCV_RACK_PATCH_DIR = "{}/presets/vcvrack".format(zynthian_engine.my_data_dir) + +class zynthian_engine_vcv_rack(zynthian_engine): + + #---------------------------------------------------------------------------- + # Initialization + #---------------------------------------------------------------------------- + + def __init__(self, zyngui=None): + super().__init__(zyngui) + + self.type = "Special" + self.name = "VCVRack" + self.nickname = "VCV" + self.jackname = "VCV Rack" + + self.options = {} + + # self.base_command = "xpra start-desktop :100 --start-child=\"{} -d\" --exit-with-children --xvfb=\"Xorg :10 vt7 -auth .Xauthority -config xrdp/xorg.conf -noreset -nolisten tcp\" --start-via-proxy=no --systemd-run=no --file-transfer=no --printing=no --resize-display=no --mdns=no --pulseaudio=no --dbus-proxy=no --dbus-control=no --webcam=no --notifications=no".format(VCV_RACK_BINARY) + self.reset() + + def start(self): + super().start() + subprocess.run(["{}/start-vcv-rack.sh".format(VCV_RACK_DIR)]) + + def stop(self): + super().stop() + subprocess.run(["{}/stop-vcv-rack.sh".format(VCV_RACK_DIR)]) + + #---------------------------------------------------------------------------- + # Bank Managament + #---------------------------------------------------------------------------- + + def get_bank_list(self, layer=None): + return self.get_filelist(VCV_RACK_PATCH_DIR, "vcv") + + + def set_bank(self, layer, bank): + self.load_bundle(bank[0]) + return True + + + def load_bundle(self, path): + with open(VCV_RACK_CONFIG, 'r') as config_file: + config = json.load(config_file) + config['patchPath'] = path + with open(VCV_RACK_CONFIG, 'w') as config_file: + json.dump(config, config_file, indent=4) + self.start() + + + # --------------------------------------------------------------------------- + # API methods + # --------------------------------------------------------------------------- + + @classmethod + def zynapi_get_banks(cls): + banks=[] + for b in cls.get_filelist(VCV_RACK_PATCH_DIR, "vcv"): + banks.append({ + 'text': b[2], + 'name': b[2], + 'fullpath': b[0], + 'raw': b, + 'readonly': False + }) + return banks + + + @classmethod + def zynapi_get_presets(cls, bank): + return [] + + + @classmethod + def zynapi_rename_bank(cls, bank_path, new_bank_name): + head, tail = os.path.split(bank_path) + new_bank_path = head + "/" + new_bank_name + os.rename(bank_path, new_bank_path) + + + @classmethod + def zynapi_remove_bank(cls, bank_path): + shutil.rmtree(bank_path) + + + @classmethod + def zynapi_download(cls, fullpath): + return fullpath + + + @classmethod + def zynapi_install(cls, dpath, bank_path): + if os.path.isdir(dpath): + shutil.move(dpath, VCV_RACK_PATCH_DIR) + + @classmethod + def zynapi_get_formats(cls): + return "vcv" \ No newline at end of file diff --git a/zyngui/zynthian_gui_engine.py b/zyngui/zynthian_gui_engine.py index 2da6be9fe..a152485a7 100644 --- a/zyngui/zynthian_gui_engine.py +++ b/zyngui/zynthian_gui_engine.py @@ -36,6 +36,7 @@ from zyngine import * from zyngine.zynthian_engine_pianoteq import * from zyngine.zynthian_engine_jalv import * +from zyngine.zynthian_engine_vcv_rack import check_vcv_rack_binary from . import zynthian_gui_config from . import zynthian_gui_selector @@ -75,6 +76,9 @@ def init_engine_info(cls): PIANOTEQ_PRODUCT, " (Demo)" if PIANOTEQ_TRIAL else "") cls.engine_info['PT'] = (PIANOTEQ_NAME, pianoteq_title, "MIDI Synth", None, zynthian_engine_pianoteq, True) + + if check_vcv_rack_binary(): + cls.engine_info['VC'] = ("VCVRack", "VCV Rack - Virtual Eurorack", "Special", None, zynthian_engine_vcv_rack, True) for plugin_name, plugin_info in get_jalv_plugins().items(): eng = 'JV/{}'.format(plugin_name) diff --git a/zyngui/zynthian_gui_layer.py b/zyngui/zynthian_gui_layer.py index 55ab6f062..02eec0361 100644 --- a/zyngui/zynthian_gui_layer.py +++ b/zyngui/zynthian_gui_layer.py @@ -243,6 +243,9 @@ def add_layer_engine(self, eng, midi_chan=None): self.index=len(self.layers)-4 self.layer_control() + elif eng.nickname == 'VCV': + self.add_layer_midich(None) + elif midi_chan is None: self.replace_layer_index=None self.zyngui.screens['midi_chan'].set_mode("ADD", 0, self.get_free_midi_chans()) From 9ff755a5f4830b090784426750a06c7192015545 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 10 Jul 2020 21:03:13 +0100 Subject: [PATCH 2/2] Autoconnect Jack audio. Load patch via CLI argument. --- zyngine/zynthian_engine_vcv_rack.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/zyngine/zynthian_engine_vcv_rack.py b/zyngine/zynthian_engine_vcv_rack.py index 2e2439ef8..8d9b9799b 100644 --- a/zyngine/zynthian_engine_vcv_rack.py +++ b/zyngine/zynthian_engine_vcv_rack.py @@ -23,6 +23,7 @@ import json import logging import subprocess +from time import sleep from . import zynthian_engine @@ -54,12 +55,14 @@ def __init__(self, zyngui=None): self.options = {} - # self.base_command = "xpra start-desktop :100 --start-child=\"{} -d\" --exit-with-children --xvfb=\"Xorg :10 vt7 -auth .Xauthority -config xrdp/xorg.conf -noreset -nolisten tcp\" --start-via-proxy=no --systemd-run=no --file-transfer=no --printing=no --resize-display=no --mdns=no --pulseaudio=no --dbus-proxy=no --dbus-control=no --webcam=no --notifications=no".format(VCV_RACK_BINARY) self.reset() - def start(self): + def start(self, patch_path): super().start() - subprocess.run(["{}/start-vcv-rack.sh".format(VCV_RACK_DIR)]) + subprocess.run(["{}/start-vcv-rack.sh".format(VCV_RACK_DIR), patch_path]) + sleep(10) + self.zyngui.zynautoconnect_midi(True) + self.zyngui.zynautoconnect_audio(True) def stop(self): super().stop() @@ -79,12 +82,16 @@ def set_bank(self, layer, bank): def load_bundle(self, path): - with open(VCV_RACK_CONFIG, 'r') as config_file: - config = json.load(config_file) - config['patchPath'] = path - with open(VCV_RACK_CONFIG, 'w') as config_file: - json.dump(config, config_file, indent=4) - self.start() + self.stop() + self.start(path) + + + #---------------------------------------------------------------------------- + # Preset Managament + #---------------------------------------------------------------------------- + + def get_preset_list(self, bank): + return [] # ---------------------------------------------------------------------------