/* Copyright (C) 2006 Christian Schneider * * This file is part of Nomad. * * Nomad 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. * * Nomad 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 Nomad; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Created on Apr 10, 2006 */ package net.sf.nmedit.jpatch.clavia.nordmodular; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import javax.swing.event.EventListenerList; import net.sf.nmedit.jpatch.ModuleDescriptions; import net.sf.nmedit.jpatch.PModuleContainer; import net.sf.nmedit.jpatch.PPatch; import net.sf.nmedit.jpatch.clavia.nordmodular.event.ModifiedListener; import net.sf.nmedit.jpatch.clavia.nordmodular.event.PAssignmentEvent; import net.sf.nmedit.jpatch.clavia.nordmodular.event.PAssignmentListener; import net.sf.nmedit.jpatch.clavia.nordmodular.event.PPatchSettingsEvent; import net.sf.nmedit.jpatch.clavia.nordmodular.event.PPatchSettingsListener; import net.sf.nmedit.jpatch.clavia.nordmodular.parser.ParseException; import net.sf.nmedit.jpatch.clavia.nordmodular.parser.PatchExporter; import net.sf.nmedit.jpatch.clavia.nordmodular.parser.PatchFileWriter; import net.sf.nmedit.jpatch.impl.PBasicPatch; import net.sf.nmedit.jsynth.Slot; import net.sf.nmedit.jsynth.clavia.nordmodular.NmSlot; import net.sf.nmedit.jsynth.clavia.nordmodular.utils.NmUtils; /** * Implementation of the (virtual) patch according to the patch file format 3.0 specification. * * @author Christian Schneider * TODO handle Micro Modular */ public class NMPatch extends PBasicPatch implements PPatch { public static final String SLOT_PROPERTY = "slot"; // setPolyphonie(1-32) /** * @see Header */ private Header header; /** * the poly voice area * @see VoiceArea */ private VoiceArea polyVoiceArea; /** * the common voice area * @see VoiceArea */ private VoiceArea commonVoiceArea; /** * midi controller settings * @see MidiControllerSet */ private MidiControllerSet midiControllerSet; /** * knob assignments * @see KnobSet */ private KnobSet knobs; /** * morph groups * @see MorphSet */ private PNMMorphSection morphSection; /** * A set of notes. The hashcode of the note class is equal to the note number. * This assures that no duplicate notes are in the set. * @see NoteSet */ private NoteSet noteSet; public final static String MODIFIED = "patch.modified"; public final static String NAME = "patch.name"; public final static String NOTE = "patch.note"; public final static String UI = "patch.ui"; public final static String VERSION = "version"; public final static String SLOT = "patch.slot"; public final static String HISTORY = "patch.history"; public final static String VAPOLY_CYCLES = "patch.va.poly.cycles"; public final static String VACOMMON_CYCLES = "patch.va.common.cycles"; private Map<String, Object> properties = new HashMap<String,Object>(); private Slot slot; private PModuleContainer[] containers; private LightProcessor lightProcessor; private EventListenerList listenerList = new EventListenerList(); private PropertyChangeSupport pcs = new PropertyChangeSupport(this); private ModifiedListener modifiedListener; private MorphAutoAssigner morphAutoAssigner; /** * Creates a new patch. */ public NMPatch(ModuleDescriptions modules) { super(modules); lightProcessor = new LightProcessor(); header = new Header(this); noteSet = new NoteSet(); midiControllerSet = new MidiControllerSet(this); knobs = new KnobSet(this); polyVoiceArea = new VoiceArea(this, true, lightProcessor, "PolyVoiceArea", Format.VALUE_SECTION_VOICE_AREA_POLY); commonVoiceArea = new VoiceArea(this, false, lightProcessor, "CommonVoiceArea", Format.VALUE_SECTION_VOICE_AREA_COMMON); morphSection = new PNMMorphSection(this); containers = new PModuleContainer[]{commonVoiceArea, polyVoiceArea, morphSection}; modifiedListener = new ModifiedListener(this); morphAutoAssigner = new MorphAutoAssigner(this); morphAutoAssigner.installAt(polyVoiceArea); morphAutoAssigner.installAt(commonVoiceArea); setProperty(VERSION, "Nord Modular patch 3.0"); setProperty(MODIFIED, Boolean.FALSE); } public void installModifiedListener() { modifiedListener.install(); } public void uninstallModifiedListener() { modifiedListener.uninstall(); } public File getFile() { try { return (File) getProperty("file"); } catch (ClassCastException e) { return null; } } public void addAssignmentListener(PAssignmentListener l) { listenerList.add(PAssignmentListener.class, l); } public void removeAssignmentListener(PAssignmentListener l) { listenerList.remove(PAssignmentListener.class, l); } protected void fireAssignmentEvent(PAssignmentEvent event) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event if (event.isAssignmentEvent()) { for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==PAssignmentListener.class) { // Lazily create the event: ((PAssignmentListener)listeners[i+1]).parameterAssigned(event); } } } else { for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==PAssignmentListener.class) { // Lazily create the event: ((PAssignmentListener)listeners[i+1]).parameterDeassigned(event); } } } } public void addPatchSettingsListener(PPatchSettingsListener l) { listenerList.add(PPatchSettingsListener.class, l); } public void removePatchSettingsListener(PPatchSettingsListener l) { listenerList.remove(PPatchSettingsListener.class, l); } public void firePatchSettingsChanged(boolean ignoreModified) { // Guaranteed to return a non-null array Object[] listeners = listenerList.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event PPatchSettingsEvent event = null; for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==PPatchSettingsListener.class) { if (event == null) event = new PPatchSettingsEvent(this); if (ignoreModified && listeners[i+1]==modifiedListener) continue; // Lazily create the event: ((PPatchSettingsListener)listeners[i+1]).patchSettingsChanged(event); } } } public PModuleContainer getModuleContainer(int index) { return containers[index]; } public int getModuleContainerCount() { return containers.length; } public Slot getSlot() { return slot; } public void setSlot(Slot slot) { Slot old = this.slot; if (old != slot) { this.slot = slot; if (old != null) { ((NmSlot)old).setPatch(null); } firePropertyChanged(SLOT_PROPERTY, old, slot); } } public PNMMorphSection getMorphSection() { return morphSection; } /** * Returns the header section of the patch. * @return the header section of the patch */ public Header getHeader() { return this.header; } /** * Returns the poly voice area. * @return the poly voice area */ public VoiceArea getPolyVoiceArea() { return this.polyVoiceArea; } /** menuLayout.getEntry("nomad.menu.file.open") .addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(new File("/home/christian/Programme/nomad/data/patch/")); chooser.setMultiSelectionEnabled(true); FileServiceTool.addChoosableFileFilters(chooser); if (!(chooser.showOpenDialog(mainWindow)==JFileChooser.APPROVE_OPTION)) return; FileService service = FileServiceTool.lookupFileService(chooser); if (service != null) { for (File file:chooser.getSelectedFiles()) { service.open(file); } } }}); * Returns the common voice area. * @return the common voice area public PatchImplementation getPatchImplementation() { return impl; } public PatchHistory getHistory() { public Module createModule( Object moduleID ) { throw new UnsupportedOperationException(); } public Connection createConnection( Connector a, Connector b ) { return null; } return (PatchHistory) getProperty(HISTORY); } */ public VoiceArea getCommonVoiceArea() { return this.commonVoiceArea; } /** * Returns the note set. * @return the note set * @see NoteSet */ public NoteSet getNoteSet() { return noteSet; } /** * Returns the midi controller settings. * @return the midi controller settings * @see MidiControllerSet */ public MidiControllerSet getMidiControllers() { return midiControllerSet; } /** * Returns the knob assignments. * @return the knob assignments. * @see KnobSet */ public KnobSet getKnobs() { return knobs; } public Object getProperty(String name) { if (NAME.equals(name)) return getName(); return properties.get(name); } public void setProperty(String name, Object value) { Object oldValue = properties.get(name); if (oldValue!=value) { if (NAME.equals(name)) { setName((String) value); return; } if (value==null) { properties.remove(name); firePropertyChanged(name, oldValue, null); } else if (!value.equals(oldValue)) { properties.put(name, value); firePropertyChanged(name, oldValue, value); if (name.equals("file") && (value instanceof File)) { setName(NmUtils.getPatchNameFromfileName((File) value)); } } } } protected void updateName(String oldname, String newname) { // called by setName(String) super.updateName(oldname, newname); firePropertyChanged(NAME, oldname, newname); } protected void firePropertyChanged(String name, Object oldValue, Object newValue) { pcs.firePropertyChange(name, oldValue, newValue); } public void addPropertyChangeListener(PropertyChangeListener l) { pcs.addPropertyChangeListener(l); } public void addPropertyChangeListener(String propertyName, PropertyChangeListener l) { pcs.addPropertyChangeListener(propertyName, l); } public void removePropertyChangeListener(PropertyChangeListener l) { pcs.removePropertyChangeListener(l); } public void removePropertyChangeListener(String propertyName, PropertyChangeListener l) { pcs.removePropertyChangeListener(propertyName, l); } public void setModified(boolean changed) { boolean oldValue = (Boolean)getProperty(MODIFIED); setProperty(MODIFIED, changed ? Boolean.TRUE : Boolean.FALSE); firePropertyChanged(MODIFIED, oldValue, changed ? Boolean.TRUE : Boolean.FALSE); } public boolean isModified() { Boolean b = (Boolean) getProperty(MODIFIED); return b != null ? b : false; } public String getNote() { return (String) getProperty(NOTE); } public void setNote(String note) { setProperty(NOTE, note); } public String getVersion() { return (String) getProperty(VERSION); } public String toString() { return getClass().getName() +"[name="+getName()+",version="+getVersion()+"]"; } public LightProcessor getLightProcessor() { return lightProcessor; } public String patchFileString() { ByteArrayOutputStream os = new ByteArrayOutputStream(); PatchFileWriter fileWriter = new PatchFileWriter(os); PatchExporter export = new PatchExporter(); try { export.export(this, fileWriter); return os.toString(); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } public static NMPatch createPatchFromFile(File file) throws ParseException, IOException { NMData data = NMData.sharedInstance(); NMPatch patch; InputStream in = new FileInputStream(file); patch = NmUtils.parsePatch(data.getModuleDescriptions(), in); in.close(); patch.setProperty("file", null); patch.setModified(false); // patch.installModifiedListener(); // enable history patch.setEditSupportEnabled(true); return patch; } public NMPatch createFromFile(File file) throws ParseException, IOException { return NMPatch.createPatchFromFile(file); } public PPatch newPatchFromFile(File file) { return null; } public NMPatch createEmptyPatch() { return new NMPatch(getModuleDescriptions()); } }