#!/usr/bin/python ############################################################################### # # flatten.py - a python script that merges all subpatches in an Ingen patch # into the parent patch # # Copyright (C) 2005 Lars Luthman # # 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 # (at your option) 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. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # ############################################################################### import ingen import os,time,sys def getPatchBounds(patch): """Returns the smallest rectangle that contains all modules in the patch.""" min_x = None min_y = None max_x = None max_y = None for node in patch.getNodes(): x = node.metadata['module-x'] y = node.metadata['module-y'] if (x != None): if (min_x == None or float(x) < min_x): min_x = float(x) if (max_x == None or float(x) > max_x): max_x = float(x) if (y != None): if (min_y == None or float(y) < min_y): min_y = float(y) if (max_y == None or float(y) > max_y): max_y = float(y) if min_x == None: min_x = 0 max_x = 0 if min_y == None: min_y = 0 max_y = 0 return (min_x, min_y, max_x, max_y) def cloneNode(om, node, patch): """Copy a node into a patch, return the new node's name.""" # create a node with a free name in the parent names = [] for node2 in patch.getNodes(): names.append(node2.getName()) for patch2 in patch.getPatches(): names.append(patch2.getName()) names.sort() name = node.getName() for name2 in names: if name2 == name: name = name + '_' om.synth.create_node.async(patch.getPath() + '/' + name, node.plugintype, node.libname, node.pluginlabel, node.polyphonic) # copy port values for port in node.getPorts(): path = '%s/%s/%s' % (patch.getPath(), name, port.getName()) om.synth.set_port_value_slow.async(path, port.value) om.metadata.set.async(path, 'user-min', '%f' % port.minvalue) om.metadata.set.async(path, 'user-max', '%f' % port.maxvalue) return name def flatten(om, patch): """Merge all subpatches into the parent patch.""" # something is wrong, we don't have a patch if patch == None: return # iterate over all subpatches for subpatch in patch.getPatches(): flatten(om, subpatch) lookup = {} # copy all nodes from the subpatch to the parent patch for node in subpatch.getNodes(): lookup[node.getName()] = cloneNode(om, node, patch) # copy all connections for node in subpatch.getNodes(): for port in node.getPorts(): if port.direction == 'OUTPUT': for target in port.getConnections().keys(): targetname = '%s/%s' % (lookup[target.getParent().getName()], target.getName()) om.synth.connect.async(patch.getPath() + '/' + lookup[node.getName()] + '/' + port.getName(), patch.getPath() + '/' + targetname) # make external connections for node in subpatch.getNodes(): if node.libname == '': lbl = node.pluginlabel if lbl == 'audio_input' or lbl == 'control_input': port1 = node.getPort('in') for port2 in port1.getConnections().keys(): dst = '%s/%s/%s' % (patch.getPath(), lookup[port2.getParent().getName()], port2.getName()) port4 = subpatch.getPort(node.getName()) conns = port4.getConnections().keys() if len(conns) == 0: portValue = port4.value om.synth.set_port_value_slow.async(dst, portValue) else: for port3 in port4.getConnections().keys(): src = port3.getPath() om.synth.connect.async(src, dst) if lbl == 'audio_output' or lbl == 'control_output': port2 = node.getPort('out', True) for port1 in port2.getConnections().keys(): src = '%s/%s/%s' % (patch.getPath(), lookup[port1.getParent().getName()], port1.getName()) port3 = subpatch.getPort(node.getName()) for port4 in port3.getConnections().keys(): dst = port4.getPath() om.synth.connect.async(src, dst) # destroy all input and output nodes from the subpatch for node in subpatch.getNodes(): if node.libname == '': lbl = node.pluginlabel if (lbl == 'audio_input' or lbl == 'control_input' or lbl == 'audio_output' or lbl == 'control_output'): om.synth.destroy_node.async('%s/%s' % (patch.getPath(), lookup[node.getName()])) # calculate where to move all the new nodes (min_x, min_y, max_x, max_y) = getPatchBounds(subpatch) sub_x = subpatch.metadata['module-x'] if sub_x == None: sub_x = 0 sub_y = subpatch.metadata['module-y'] if sub_y == None: sub_y = 0 x_offset = float(sub_x) if min_x != None: x_offset = float(sub_x) - min_x y_offset = float(sub_y) if min_y != None: y_offset = float(sub_y) - min_y # move the new nodes for node in subpatch.getNodes(): x = float(node.metadata['module-x']) if x == None: x = 0 om.metadata.set.async('%s/%s' % (patch.getPath(), lookup[node.getName()]), 'module-x', '%f' % (x + x_offset)) y = float(node.metadata['module-y']) if y == None: y = 0 om.metadata.set.async('%s/%s' % (patch.getPath(), lookup[node.getName()]), 'module-y', '%f' % (y + y_offset)) # move the old nodes in the patch x_offset = 0 if min_x != None and max_x != None: x_offset = max_x - min_x y_offset = 0 if min_y != None and max_y != None: y_offset = max_y - min_y for node in patch.getNodes(): if node.getName() not in lookup.values(): x = node.metadata['module-x'] if x != None and float(x) > float(sub_x): om.metadata.set.async(node.getPath(), 'module-x', '%f' % (float(x) + x_offset)) y = node.metadata['module-y'] if y != None and float(y) > float(sub_y): om.metadata.set.async(node.getPath(), 'module-y', '%f' % (float(y) + y_offset)) # destroy the subpatch om.synth.destroy_patch(subpatch.getPath()) def main(om): om.setEnvironment(ingen.Environment()) om.engine.activate.async() om.engine.load_plugins.async() om.request.all_objects(om.getAddressAsString()) # wait for all the data to arrive (there should be a cleaner way) time.sleep(3) patch = om.getEnvironment().getPatch(sys.argv[1], True); flatten(om, patch) os._exit(0) if len(sys.argv) > 1: if sys.argv[1] == "--name": print "Flatten patch" os._exit(0) elif sys.argv[1] == "--shortdesc": print "Merge the contents of all subpatches into the parent patch"; os._exit(0) elif sys.argv[1] == "--signature": print "%p"; os._exit(0) else: ingen.startClient(main) else: print "Which patch do you want to flatten?" os._exit(0)