/* 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
*/
package net.sf.nmedit.jsynth.clavia.nordmodular;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import net.sf.nmedit.jnmprotocol2.AckMessage;
import net.sf.nmedit.jnmprotocol2.ErrorMessage;
import net.sf.nmedit.jnmprotocol2.IAmMessage;
import net.sf.nmedit.jnmprotocol2.KnobAssignmentMessage;
import net.sf.nmedit.jnmprotocol2.LightMessage;
import net.sf.nmedit.jnmprotocol2.MeterMessage;
import net.sf.nmedit.jnmprotocol2.MorphRangeChangeMessage;
import net.sf.nmedit.jnmprotocol2.NewPatchInSlotMessage;
import net.sf.nmedit.jnmprotocol2.NmProtocolListener;
import net.sf.nmedit.jnmprotocol2.NoteMessage;
import net.sf.nmedit.jnmprotocol2.ParameterMessage;
import net.sf.nmedit.jnmprotocol2.ParameterSelectMessage;
import net.sf.nmedit.jnmprotocol2.PatchListMessage;
import net.sf.nmedit.jnmprotocol2.PatchMessage;
import net.sf.nmedit.jnmprotocol2.RequestPatchMessage;
import net.sf.nmedit.jnmprotocol2.SetPatchTitleMessage;
import net.sf.nmedit.jnmprotocol2.SlotActivatedMessage;
import net.sf.nmedit.jnmprotocol2.SlotsSelectedMessage;
import net.sf.nmedit.jnmprotocol2.SynthSettingsMessage;
import net.sf.nmedit.jnmprotocol2.VoiceCountMessage;
import net.sf.nmedit.jpatch.InvalidDescriptorException;
import net.sf.nmedit.jpatch.PModule;
import net.sf.nmedit.jpatch.PModuleContainer;
import net.sf.nmedit.jpatch.PParameter;
import net.sf.nmedit.jpatch.clavia.nordmodular.Format;
import net.sf.nmedit.jpatch.clavia.nordmodular.Knob;
import net.sf.nmedit.jpatch.clavia.nordmodular.NMPatch;
import net.sf.nmedit.jpatch.clavia.nordmodular.VoiceArea;
import net.sf.nmedit.jpatch.clavia.nordmodular.parser.Helper;
import net.sf.nmedit.jpatch.clavia.nordmodular.parser.ParseException;
import net.sf.nmedit.jpatch.clavia.nordmodular.parser.PatchBuilder;
import net.sf.nmedit.jsynth.Slot;
import net.sf.nmedit.jsynth.SynthException;
import net.sf.nmedit.jsynth.clavia.nordmodular.utils.BitstreamPatchParser;
import net.sf.nmedit.jsynth.clavia.nordmodular.utils.NmUtils.ParserErrorHandler;
import net.sf.nmedit.jsynth.clavia.nordmodular.worker.ScheduledMessage;
import net.sf.nmedit.jsynth.event.SlotEvent;
import net.sf.nmedit.jsynth.event.SlotListener;
import net.sf.nmedit.jsynth.event.SlotManagerListener;
import net.sf.nmedit.jsynth.event.SynthesizerEvent;
import net.sf.nmedit.jsynth.event.SynthesizerStateListener;
public class NmMessageHandler extends NmProtocolListener
implements SlotManagerListener, PropertyChangeListener, SlotListener, SynthesizerStateListener
{
private NordModular synth;
public NmMessageHandler(NordModular synth)
{
this.synth = synth;
synth.addSynthesizerStateListener(this);
synth.getSlotManager().addSlotManagerListener(this);
}
private boolean isValidSlot(int slotId)
{
return 0<=slotId && slotId<synth.getSlotCount();
}
public void messageReceived(SynthSettingsMessage message)
{
synth.setSettings(message);
}
public void messageReceived(MorphRangeChangeMessage message)
{
int slotId = message.getSlot();
if (!isValidSlot(slotId))
return;
int vaId = message.getSection();
int moduleId = message.getModule();
int paramId = message.getParameter();
int span = message.getSpan();
int direction = message.getDirection(); // +==0/-==1
NmSlot slot = synth.getSlot(slotId);
NMPatch patch = slot.getPatch();
VoiceArea va = null;
if (vaId == Format.VALUE_SECTION_VOICE_AREA_POLY)
va = patch.getPolyVoiceArea();
else if (vaId == Format.VALUE_SECTION_VOICE_AREA_COMMON)
va = patch.getCommonVoiceArea();
if (va == null)
return;
PModule module = va.getModule(moduleId);
PParameter p;
try
{
p = Helper.getParameter(module, "morph", paramId);
}
catch (InvalidDescriptorException e)
{
return;
}
p.setValue(direction==1?-span:+span);
}
public void messageReceived(NoteMessage message)
{
System.out.println("note: "+message);
}
/**
* This message is ignored. NordModular.connect() causes this message and
* handles it itself.
*/
public void messageReceived(IAmMessage message)
{
// no op
}
/**
* This message is ignored. The GetPatchWorker causes this kind
* of messages and handles them itself.
*/
public void messageReceived(PatchMessage message)
{
// get patch name
String patchName = message.getPatchNameIfPresent();
if (patchName != null)
{
// patch name is part of message
synth
.getSlot(message.getSlot())
.setPatchNameValue(patchName);
}
if (message.containsSection(Format.S_HEADER))
{
int slotId = message.getSlot();
NmSlot slot = synth.getSlot(slotId);
NMPatch patch = slot.getPatch();
if (patch == null)
return;
PatchBuilder builder = new PatchBuilder(patch, new ParserErrorHandler(), synth.getModuleDescriptions());
BitstreamPatchParser bpp = new BitstreamPatchParser();
bpp.setRecognizedSections(Format.S_HEADER);
try
{
bpp.transcode(message.getPatchStream(), builder);
} catch (ParseException e)
{
e.printStackTrace();
}
}
}
/**
* This message is ignored.
*/
public void messageReceived(PatchListMessage message)
{
// no op
}
public void messageReceived(SetPatchTitleMessage message)
{
int slotId = message.getSlot();
//int pid = message.get("pid");
// check if slot is available <=> synth is connected
// -> this is not implied by the received message
if (!isValidSlot(slotId))
return;
NmSlot slot = synth.getSlot(slotId);
NMPatch patch = slot.getPatch();
String title = message.getTitle();
if (patch != null) patch.setName(title);
slot.setPatchNameValue(title);
}
/**
* TODO document for messageReceived(AckMessage)
*/
public void messageReceived(AckMessage message)
{
int slotId = message.getSlot();
// check if slot is available <=> synth is connected
// -> this is not implied by the received message
if (!isValidSlot(slotId))
return;
// request patch name
NmSlot slot = synth.getSlot(slotId);
slot.updatePatchName();
}
public void messageReceived(NewPatchInSlotMessage message)
{
// user changed the patch in one of the slots
// trigger-source: synth
int slotId = message.getSlot();
if (!isValidSlot(slotId))
return;
int patchId = message.getPid();
NmSlot slot = synth.getSlot(slotId);
NMPatch patch = slot.getPatch();
if (patch != null) patch.setSlot(null);
slot.setPatch(null);
//slot.setPatchId(patchId);
//getPatch(slotId, patchId);
try
{
slot.requestPatch();
} catch (SynthException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*
private void getPatch(int slot, int patchId)
{
if (!synth.isConnected())
return;
GetPatchWorker worker = new GetPatchWorker(synth, slot, patchId);
synth.getScheduler().offer(worker);
}*/
public static void requestPatch(NordModular synth, int slotId)
{
synth.getScheduler().offer(new ScheduledMessage(synth, new RequestPatchMessage(slotId)));
}
public void messageReceived(VoiceCountMessage message)
{
for (int i=0;i<synth.getSlotCount();i++)
{
synth.getSlot(i).setVoiceCount(message.getVoiceCount(i));
}
}
// slot selected => led on
// slot activated => led blinking
public void messageReceived(SlotsSelectedMessage message)
{
for (int i=0;i<synth.getSlotCount();i++)
{
NmSlot slot = synth.getSlot(i);
boolean enabled = message.isSlotSelected(i);
slot.setEnabledValue(enabled);
slot.updatePatchName();
}
}
public void messageReceived(SlotActivatedMessage message)
{
int slotId = message.getActiveSlot();
if (!isValidSlot(slotId))
return;
synth.getNmSlotManager().setActiveSlot(slotId);
}
public void messageReceived(LightMessage message)
{
int slotId = message.getSlot();
if (!isValidSlot(slotId))
return;
NmSlot slot = synth.getSlot(slotId);
NMPatch patch = slot.getPatch();
if (patch == null) return;
patch.getLightProcessor().processLightMessage(message);
}
public void messageReceived(MeterMessage message)
{
int slotId = message.getSlot();
if (!isValidSlot(slotId))
return;
NmSlot slot = synth.getSlot(slotId);
NMPatch patch = slot.getPatch();
if (patch == null) return;
patch.getLightProcessor().processMeterMessage(message);
}
public void messageReceived(KnobAssignmentMessage message)
{
int slotId = message.getSlot();
if (!isValidSlot(slotId))
return;
int prevKnob = message.get("prevknob");
int vaId = message.get("section");
int moduleId = message.get("module");
int paramId = message.get("parameter");
int knob = message.get("knob");
NmSlot slot = synth.getSlot(slotId);
NMPatch patch = slot.getPatch();
if (prevKnob>=0)
{
Knob k = patch.getKnobs().getByID(prevKnob);
if (k != null)
k.setParameter(null);
}
if (knob>=0)
{
VoiceArea va = null;
if (vaId == Format.VALUE_SECTION_VOICE_AREA_POLY)
va = patch.getPolyVoiceArea();
else if (vaId == Format.VALUE_SECTION_VOICE_AREA_COMMON)
va = patch.getCommonVoiceArea();
if (va == null)
return;
PModule module = va.getModule(moduleId);
PParameter p;
try
{
p = Helper.getParameter(module, "parameter", paramId);
}
catch (InvalidDescriptorException e)
{
return;
}
Knob k = patch.getKnobs().getByID(knob);
if (k != null)
k.setParameter(p);
}
}
public void messageReceived(ParameterSelectMessage message)
{
int slotId = message.getSlot();
if (!isValidSlot(slotId))
return;
int vaId = message.getSection();
int moduleId = message.getModule();
int paramId = message.getParameter();
// addParameter("pid", "data:pid");
// addParameter("sc", "data:sc");
NmSlot slot = synth.getSlot(slotId);
NMPatch patch = slot.getPatch();
String parameterClass = "parameter";
VoiceArea va = null;
if (vaId == Format.VALUE_SECTION_VOICE_AREA_POLY)
va = patch.getPolyVoiceArea();
else if (vaId == Format.VALUE_SECTION_VOICE_AREA_COMMON)
va = patch.getCommonVoiceArea();
else if (vaId == Format.VALUE_SECTION_MORPH)
{
parameterClass = "morph";
va = patch.getPolyVoiceArea();
}
if (va == null)
return;
PModule module = va.getModule(moduleId);
PParameter p;
try
{
p = Helper.getParameter(module, parameterClass, paramId);
}
catch (InvalidDescriptorException e)
{
return;
}
p.requestFocus();
}
public void messageReceived(ParameterMessage message)
{
int slotId = message.getSlot();
if (!isValidSlot(slotId))
return;
int vaId = message.getSection();
int moduleId = message.getModule();
int paramId = message.getParameter();
int value = message.getValue();
// addParameter("pid", "data:pid");
// addParameter("sc", "data:sc");
NmSlot slot = synth.getSlot(slotId);
NMPatch patch = slot.getPatch();
PModuleContainer va = null;
String parameterClass = "parameter";
if (vaId == Format.VALUE_SECTION_VOICE_AREA_POLY)
va = patch.getPolyVoiceArea();
else if (vaId == Format.VALUE_SECTION_VOICE_AREA_COMMON)
va = patch.getCommonVoiceArea();
else if (vaId == Format.VALUE_SECTION_MORPH)
{
parameterClass = "morph";
va = patch.getMorphSection();
}
else
{
// should not happen
return;
}
PModule module = va.getModule(moduleId);
PParameter p;
try
{
p = Helper.getParameter(module, parameterClass, paramId);
}
catch (InvalidDescriptorException e)
{
return;
}
p.setValue(value);
return;
}
public void messageReceived(ErrorMessage message)
{
// TODO let other listeners receive error message
//throw new RuntimeException(message.toString());
}
public void slotAdded(SlotEvent e)
{
installSlot((NmSlot) e.getSlot());
}
public void slotRemoved(SlotEvent e)
{
uninstallSlot((NmSlot) e.getSlot());
}
private void installSlot(NmSlot slot)
{
slot.addSlotListener(this);
slot.addPropertyChangeListener(Slot.ENABLED_PROPERTY, this);
}
private void uninstallSlot(NmSlot slot)
{
slot.removeSlotListener(this);
slot.removePropertyChangeListener(this);
}
public void propertyChange(PropertyChangeEvent evt)
{
NmSlot slot = (NmSlot) evt.getSource();
if (evt.getPropertyName() == Slot.ENABLED_PROPERTY)
{
if ((Boolean)evt.getNewValue())
{
// enabled
// TODO should we call request patch first to get the pid ?
//getPatch(slot.getSlotId(), 0);
//requestPatch(synth, slot.getSlotId());
}
else
{
// disabled
//slot.setPatch(null);
}
}
}
public void newPatchInSlot(SlotEvent e)
{
NmSlot slot = (NmSlot) e.getSlot();
NMPatch oldPatch = (NMPatch) e.getOldPatch();
NMPatch newPatch = (NMPatch) e.getNewPatch();
uninstallSynchronizer(slot.getSlotId());
if (newPatch != null)
{
installSynchronizer(slot, newPatch);
}
}
private void installSynchronizer(NmSlot slot, NMPatch patch)
{
NmPatchSynchronizer sync = new NmPatchSynchronizer(slot.getSynthesizer(), patch, slot);
synchronizerList[slot.getSlotIndex()] = sync;
sync.install();
}
private void uninstallSynchronizer(int slotId)
{
NmPatchSynchronizer sync = synchronizerList[slotId];
if (sync!=null)
{
synchronizerList[slotId] = null;
sync.uninstall();
}
}
private NmPatchSynchronizer[] synchronizerList = new NmPatchSynchronizer[4];
public void synthConnectionStateChanged(SynthesizerEvent e)
{
if (synth.isConnected())
{
// reply are ACK messages
for (int i=0;i<synth.getSlotCount();i++)
NmMessageHandler.requestPatch(synth, i);
}
}
}