/* 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 Dec 20, 2006
*/
package net.sf.nmedit.jpatch.clavia.nordmodular.parser;
import java.util.List;
import java.util.NoSuchElementException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import net.sf.nmedit.jpatch.InvalidDescriptorException;
import net.sf.nmedit.jpatch.ModuleDescriptions;
import net.sf.nmedit.jpatch.PConnector;
import net.sf.nmedit.jpatch.PModule;
import net.sf.nmedit.jpatch.PParameter;
import net.sf.nmedit.jpatch.clavia.nordmodular.Format;
import net.sf.nmedit.jpatch.clavia.nordmodular.Header;
import net.sf.nmedit.jpatch.clavia.nordmodular.Knob;
import net.sf.nmedit.jpatch.clavia.nordmodular.MidiController;
import net.sf.nmedit.jpatch.clavia.nordmodular.MidiControllerSet;
import net.sf.nmedit.jpatch.clavia.nordmodular.NMPatch;
import net.sf.nmedit.jpatch.clavia.nordmodular.Note;
import net.sf.nmedit.jpatch.clavia.nordmodular.PNMMorphSection;
import net.sf.nmedit.jpatch.clavia.nordmodular.Signal;
import net.sf.nmedit.jpatch.clavia.nordmodular.VoiceArea;
public class PatchBuilder implements PContentHandler
{
private static Log log = LogFactory.getLog(PatchBuilder.class);
private NMPatch patch;
private VoiceArea voiceArea;
private ModuleDescriptions modules;
private ErrorHandler errorHandler;
private String patchName = null;
public PatchBuilder(ErrorHandler errorHandler, ModuleDescriptions modules)
{
this(null, errorHandler, modules);
}
public PatchBuilder(NMPatch patch, ErrorHandler errorHandler, ModuleDescriptions modules)
{
this.patch = patch;
this.errorHandler = errorHandler;
this.modules = modules;
reset();
}
public String getPatchName()
{
return patchName;
}
public void setPatchName(String name)
{
this.patchName = name;
if (this.patch != null)
patch.setName(name);
}
private void emitwarning(String message) throws ParseException
{
errorHandler.warning(new ParseException(message));
}
private void emiterror(String message) throws ParseException
{
errorHandler.error(new ParseException(message));
}
private void emitfatal(String message) throws ParseException
{
errorHandler.fatal(new ParseException(message));
}
public void reset()
{
patch = new NMPatch(modules);
patch.setName(getPatchName());
patch.setEditSupportEnabled(false); // disable history
}
public NMPatch getPatch()
{
return patch;
}
public void notes( String notes ) throws ParseException
{
patch.setNote(notes);
}
private VoiceArea getVoiceArea(int voiceAreaId)
{
switch (voiceAreaId)
{
case 1: return patch.getPolyVoiceArea();
case 0: return patch.getCommonVoiceArea();
default: return null;
}
}
public void beginSection( int section, int voiceAreaId )
throws ParseException
{
voiceArea = getVoiceArea(voiceAreaId);
}
public void endSection( int section ) throws ParseException
{
voiceArea = null;
}
public void header( int[] record ) throws ParseException
{
Header header = patch.getHeader();
for (int i=0;i<HEADER_RSIZE;i++)
header.setValue(i, record[i]);
}
public void header( String property, String value ) throws ParseException
{
if ("version".equals(property))
{
if (!value.contains("3"))
emitfatal("unsupported version "+value);
}
else
{
emitwarning("unknown property "+property+"="+value);
}
}
public void moduleDump( int[] record ) throws ParseException
{
PModule module;
int mindex = record[1];
try
{
module = voiceArea.createModule(modules.getModuleById("m"+mindex));
}
catch (InvalidDescriptorException e)
{
throw new ParseException(e);
}
module.setInternalLocation(record[2], record[3]);
if (!voiceArea.add(record[0], module))
{
String e = module+" rejected in "+voiceArea;
PModule prev = voiceArea.getModule(mindex);
if (prev != null)
e+=" index reserved by "+prev;
emitwarning(e);
}
}
public void currentNoteDump( int[] record ) throws ParseException
{
patch.getNoteSet().add(new Note(record[0], record[1], record[2]));
}
private boolean intToOutput(int connectorType) throws ParseException
{
// 0 ~ input, 1 ~ output
switch (connectorType)
{
case 0: return false;
case 1: return true;
default: throw new ParseException("invalid connector type:"+connectorType);
}
}
private PConnector getConnector(int mod, int cindex, int ctype) throws ParseException
{
PModule module = voiceArea.getModule(mod);
if (module == null)
{
emiterror("module[index="+mod+"] does not exist");
return null;
}
boolean output = intToOutput(ctype);
for (int i=module.getConnectorCount()-1;i>=0;i--)
{
PConnector tmp = module.getConnector(i);
if (cindex==tmp.getIntAttribute("index",-1) && tmp.isOutput()==output)
{
return tmp;
}
}
emiterror("Connector[index="+cindex
+",output="+output+"("+ctype+")] does not exist in "+module);
return null;
}
public void cableDump( int[] record ) throws ParseException
{
Signal signal = Signal.bySignalID(record[0]);
PConnector cdst = getConnector(record[1], record[2], record[3]);
PConnector csrc = getConnector(record[4], record[5], record[6]);
if (cdst == null || csrc == null)
{
return;
}
if (cdst.isConnected(csrc))
emitwarning("Already connected: "+csrc+", "+cdst);
else
{
if (!csrc.connect(cdst/*, signal*/)) // TODO signal
emiterror("Could not connect: "+csrc+", "+cdst);
}
}
public void parameterDump( int[] record ) throws ParseException
{
PModule module = voiceArea.getModule(record[0]);
if (module.getIntAttribute("index", -1)!=record[1])
emiterror(module+" has different id than "+record[1]+" in ParameterDump");
List<PParameter> plist = Helper.getParametersByClass(module, "parameter");
int paramClassCount = plist.size();
if (record[2]!=paramClassCount)
emiterror("invalid number of parameters[class='parameter'] "+record[2]+" expected "+paramClassCount);
for (int i=0;i<paramClassCount;i++)
{
plist.get(i).setValue(record[3+i]);
}
}
public void customDump( int[] record ) throws ParseException
{
PModule module = voiceArea.getModule(record[0]);
List<PParameter> plist = Helper.getParametersByClass(module, "custom");
int customCount = plist.size();
if (record[1]!=customCount)
emiterror("invalid number of parameters[class=custom] "+record[2]+" expected "+customCount);
for (int i=0;i<customCount;i++)
{
plist.get(i).setValue(record[2+i]);
}
}
public void keyboardAssignment( int[] record ) throws ParseException
{
PNMMorphSection morphs = patch.getMorphSection();
for (int i=0;i<4;i++)
{
int value = record[i];
PParameter m = morphs.getKeyboardAssignment(i);
if (value>=0 && value<=2)
m.setValue(value);
else
emiterror("morph "+i
+" keyboard assignment value out of range ["
+m.getMinValue()+".."+m.getMaxValue()+"]: "+value);
}
}
public void knobMapDump( int[] record ) throws ParseException
{
VoiceArea va = getVoiceArea(record[0]);
int modIndex = record[1];
int paramIndex = record[2];
PParameter p;
if (va == null)
{
p = patch.getMorphSection().getMorph(paramIndex);
}
else
{
PModule module = va.getModule(modIndex);
if (module == null)
throw new ParseException("[KnobMapDump] Module does not exist at index:"+modIndex);
p = Helper.getParameter(module, "parameter", paramIndex);
if (p == null)
throw new ParseException("[KnobMapDump] Parameter does not exist at index:"+paramIndex+" (module index: "+modIndex+")");
}
Knob knob = patch.getKnobs().getByID(record[3]);
knob.setParameter(p);
}
public void morphMapDumpProlog( int[] record ) throws ParseException
{
PNMMorphSection morphs = patch.getMorphSection();
for (int i=0;i<4;i++)
{
int value = record[i];
PParameter m = morphs.getMorph(i);
if (value>=m.getMinValue() && value<=m.getMaxValue())
m.setValue(value);
else
emiterror("morph "+i
+" value out of range ["+m.getMinValue()+".."+m.getMaxValue()+"]: "+value);
}
}
public void morphMapDump( int[] record ) throws ParseException
{
VoiceArea voiceArea = getVoiceArea(record[0]);
PModule module = voiceArea.getModule(record[1]);
PParameter p = Helper.getParameter(module, "parameter", record[2]);
PParameter morphRange = p.getExtensionParameter();
if (morphRange == null)
{
if (log.isDebugEnabled())
log.debug("morphMapDump, morph parameter not found for parameter "+p);
}
else
{
patch.getMorphSection().assign(record[3], p);
morphRange.setValue(record[4]);
}
}
public void ctrlMapDump( int[] record ) throws ParseException
{
MidiControllerSet mcset = patch.getMidiControllers();
int ccId = record[Format.CTRL_MAP_DUMP_CC_INDEX];
if (!MidiController.isValidCC(ccId))
emiterror("invalid cc number "+ccId);
else
{
MidiController cc = mcset.getByMC(ccId);
PParameter p = null;
int section = record[Format.CTRL_MAP_DUMP_SECTION_INDEX];
int moduleIndex = record[Format.CTRL_MAP_DUMP_MODULE_INDEX];
int pindex = record[Format.CTRL_MAP_DUMP_PARAMETER_INDEX];
if (section == 0 || section == 1)
{
VoiceArea va = section == 0 ? patch.getCommonVoiceArea() : patch.getPolyVoiceArea();
PModule module = va.getModule(moduleIndex);
if (module == null)
emiterror("Module[index="+moduleIndex+"] does not exist in "+patch.getCommonVoiceArea());
if (pindex<0 || pindex>=Helper.getParameterClassCount(module, "parameter"))
emiterror(module+" has no parameter[index="+pindex+"]");
else
p = Helper.getParameter(module, "parameter", pindex);
}
else if (section==2)
p = patch.getMorphSection().getMorph(record[2]);
else
emiterror("invalid section-id "+section);
cc.setParameter(p);
}
}
public void moduleNameDump( int moduleIndex, String moduleName )
throws ParseException
{
PModule module = null;
try
{
module = voiceArea.getModule(moduleIndex);
}
catch (NoSuchElementException e)
{}
if (module != null)
{
module.setTitle(moduleName);
}
else
{
emiterror("namedump: module[index="
+moduleIndex+",name='"+moduleName+"'] does not exist");
}
}
public void beginDocument() throws ParseException
{
// no op
}
public void endDocument() throws ParseException
{
// no op
}
}