diff --git a/zyngine/zynthian_state_manager.py b/zyngine/zynthian_state_manager.py index 98b91cea6..5339e5ef3 100644 --- a/zyngine/zynthian_state_manager.py +++ b/zyngine/zynthian_state_manager.py @@ -1087,7 +1087,7 @@ def save_snapshot(self, fpath, extra_data=None): self.end_busy("save snapshot") return True - def load_snapshot(self, fpath, load_chains=True, load_sequences=True, merge=False): + def load_snapshot(self, fpath, load_chains=True, load_sequences=True, load_ctrldev=False, merge=False): """Loads a snapshot from file fpath : Full path and filename of snapshot file @@ -1217,7 +1217,7 @@ def load_snapshot(self, fpath, load_chains=True, load_sequences=True, merge=Fals zs3 = self.sanitize_zs3_from_json(state["zs3"]) if not merge: self.zs3 = zs3 - self.load_zs3(zs3["zs3-0"], autoconnect=False) + self.load_zs3(zs3["zs3-0"], autoconnect=False, load_ctrldev=load_ctrldev) try: mute |= self.zs3["zs3-0"]["mixer"]["chan_16"]["mute"] except: @@ -1240,6 +1240,12 @@ def load_snapshot(self, fpath, load_chains=True, load_sequences=True, merge=Fals for proc in self.chain_manager.processors.values(): proc.set_midi_autolearn(True) + if load_ctrldev and not load_chains: + zs3 = self.sanitize_zs3_from_json(state["zs3"]) + if not merge: + self.zs3 = zs3 + self.load_ctrldev(zs3["zs3-0"]) + # Save last snapshot info and get snapshot's program number self.last_snapshot_count += 1 if basename(fpath) != "last_state.zss": @@ -1337,7 +1343,7 @@ def save_last_state_snapshot(self): def load_last_state_snapshot(self): if isfile(self.last_state_snapshot_fpath): - return self.load_snapshot(self.last_state_snapshot_fpath) + return self.load_snapshot(self.last_state_snapshot_fpath, load_ctrldev=True) def delete_last_state_snapshot(self): try: @@ -1377,7 +1383,7 @@ def toggle_zs3_chain_restore_flag(self, zs3_id, chain_id): except: tstate["restore"] = False - def load_zs3(self, zs3_id, autoconnect=True): + def load_zs3(self, zs3_id, autoconnect=True, load_ctrldev=False): """Restore a ZS3 zs3_id : ID of ZS3 to restore or zs3 dict @@ -1533,7 +1539,7 @@ def load_zs3(self, zs3_id, autoconnect=True): if "midi_capture" in zs3_state: self.set_busy_details("restoring midi capture state") - self.set_midi_capture_state(zs3_state['midi_capture']) + self.set_midi_capture_state(zs3_state['midi_capture'], load_ctrldev=load_ctrldev) if "global" in zs3_state: if "midi_transpose" in zs3_state["global"]: @@ -1570,6 +1576,78 @@ def load_zs3(self, zs3_id, autoconnect=True): zynautoconnect.request_audio_connect(True) return True + def load_ctrldev(self, zs3_id, autoconnect=True, load_ctrldev=True): + """Restore ctrldev from a zs3 + + zs3_id : ID of ZS3 to restore or zs3 dict + Returns : True on success + """ + + if isinstance(zs3_id, str): + # Try loading exact match + try: + zs3_state = self.zs3[zs3_id] + except: + # else ignore MIDI channel => try loading "program change" match + try: + zs3_id = f"*/{zs3_id.split('/')[1]}" + zs3_state = self.zs3[zs3_id] + except: + logging.info(f"Not found ZS3 matching '{zs3_id}'") + return False + else: + try: + zs3_state = zs3_id + zs3_id = self.last_zs3_id + if zs3_id is None: + zs3_id = "zs3-0" + except: + zs3_id = "zs3-0" + + if "midi_capture" in zs3_state: + self.set_busy_details("restoring midi capture state") + """Set midi input (capture) state: flags, chain routing, etc. + + mcstate : dictionary with state. None for reset state to defaults. + """ + mcstate = zs3_state['midi_capture'] + if mcstate: + ctrldev_state_drivers = {} + for uid, state in mcstate.items(): + #logging.debug(f"MCSTATE {uid} => {state}") + izmip = zynautoconnect.get_midi_in_devid_by_uid(uid, zynthian_gui_config.midi_usb_by_port) + if izmip is None: + continue + zynautoconnect.update_midi_in_dev_mode(izmip) + try: + # TODO: Use ctrldev_driver=None to disable driver + if state["disable_ctrldev"]: + self.ctrldev_manager.unload_driver(izmip, True) + else: + self.ctrldev_manager.load_driver(izmip, state["ctrldev_driver"]) + except: + pass + try: + ctrldev_state_drivers[uid] = state["ctrldev_state"] + except: + pass + + self.ctrldev_manager.set_state_drivers(ctrldev_state_drivers) + + else: + zynautoconnect.reset_midi_in_dev_all() + + + if zs3_id != 'zs3-0': + self.last_zs3_id = zs3_id + #self.zs3['zs3-0'] = self.zs3[zs3_id].copy() + zynsigman.send(zynsigman.S_STATE_MAN, self.SS_LOAD_ZS3, zs3_id=zs3_id) + + if autoconnect: + zynautoconnect.request_midi_connect(True) + zynautoconnect.request_audio_connect(True) + return True + def get_next_zs3_index(self): used_indexes = [] for zid, state in self.zs3.items(): @@ -1951,7 +2029,7 @@ def get_midi_capture_state(self): return mcstate - def set_midi_capture_state(self, mcstate=None): + def set_midi_capture_state(self, mcstate=None, load_ctrldev=False): """Set midi input (capture) state: flags, chain routing, etc. mcstate : dictionary with state. None for reset state to defaults. @@ -1984,8 +2062,11 @@ def set_midi_capture_state(self, mcstate=None): # TODO: Use ctrldev_driver=None to disable driver if state["disable_ctrldev"]: self.ctrldev_manager.unload_driver(izmip, True) - else: + elif load_ctrldev: self.ctrldev_manager.load_driver(izmip, state["ctrldev_driver"]) + else: + pass + # self.ctrldev_manager.load_driver(izmip, state["ctrldev_driver"]) except: pass try: diff --git a/zyngui/zynthian_gui_snapshot.py b/zyngui/zynthian_gui_snapshot.py index 2bb215154..773b4b409 100644 --- a/zyngui/zynthian_gui_snapshot.py +++ b/zyngui/zynthian_gui_snapshot.py @@ -295,6 +295,7 @@ def show_options(self, i, restrict_options): "Load Replace Chains": [param, ["Load chains from snapshot, replacing current state.", "snapshot_chains.png"]], "Load Merge Chains": [param, ["Load chains from snapshot, merging with current state.", "snapshot_chains.png"]], "Load Replace Sequences": [param, ["Load sequences from snapshot, replacing current state", "snapshot_sequences.png"]], + "Load MIDI device drivers": [param, ["Load MIDI device drivers from snapshot, replacing current drivers", "midi_input.png"]], "Save Overwriting": [param, ["Save current state, overwriting this snapshot.", "snapshot_overwrite.png"]] } budir = dirname(fpath) + "/.backup" @@ -327,6 +328,9 @@ def options_cb(self, option, param): elif option == "Load Replace Sequences": # self.zyngui.show_confirm("Loading sequences from '%s' will destroy current sequences..." % (fname), self.load_snapshot_sequences, fpath) self.load_snapshot_sequences(fpath) + elif option == "Load MIDI device drivers": + # self.zyngui.show_confirm("Loading ctrldev drivers from '%s' will destroy current drivers..." % (fname), self.load_snapshot_ctrldev, fpath) + self.load_snapshot_ctrldev(fpath) elif option == "Save Overwriting": # self.zyngui.show_confirm("Do you really want to overwrite '%s'?" % (fname), self.save_snapshot, fpath) self.save_snapshot(fpath) @@ -367,6 +371,12 @@ def load_snapshot_sequences(self, fpath): self.sm.load_snapshot(fpath, load_chains=False) self.zyngui.show_screen('zynpad', hmode=self.zyngui.SCREEN_HMODE_RESET) + def load_snapshot_ctrldev(self, fpath): + if self.is_not_empty_snapshot() and fpath != self.sm.last_state_snapshot_fpath: + self.sm.save_last_state_snapshot() + self.sm.load_snapshot(fpath, load_chains=False, load_sequences=False, load_ctrldev=True) + self.zyngui.show_screen('audio_mixer', hmode=self.zyngui.SCREEN_HMODE_RESET) + def restore_backup_cb(self, fname, fpath): logging.debug("Restoring snapshot backup '{}'".format(fname)) self.load_snapshot(fpath) diff --git a/zynthian_headless.py b/zynthian_headless.py index 537d91d6a..a1ebafd75 100644 --- a/zynthian_headless.py +++ b/zynthian_headless.py @@ -31,8 +31,7 @@ def __init__(self): self.chain_manager = self.state_manager.chain_manager if zynthian_gui_config.restore_last_state: - snapshot_loaded = self.state_manager.load_snapshot( - "/zynthian/zynthian-my-data/snapshots/last_state.zss") + snapshot_loaded = self.state_manager.load_last_state_snapshot() self.state_manager.init_midi() self.state_manager.init_midi_services()