package advanced.gestureSound.gestures; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import org.mt4j.input.IMTInputEventListener; import org.mt4j.input.inputData.AbstractCursorInputEvt; import org.mt4j.input.inputData.InputCursor; import org.mt4j.input.inputData.MTInputEvent; import processing.core.PApplet; import Jama.Matrix; import advanced.gestureSound.gestures.filters.KalmanFilter; import advanced.gestureSound.gestures.qualities.Curvature; import advanced.gestureSound.gestures.qualities.Quality; import advanced.gestureSound.gestures.qualities.Velocity; import advanced.gestureSound.input.InputDelegate; import de.sciss.jcollider.Synth; public class GestureEngine { public static class SynthInfo { public Synth synth; public String parameter; public ParamMap pMap; public Zone zone; public SynthInfo(Synth synth, String param, ParamMap pMap, Zone z) { this.synth = synth; this.parameter = param; this.pMap = pMap; this.zone = z; } } public static interface ParamMap { /** * Map from the range [-1..1] to whatever the synth param takes * @param in * @return */ public float map(float in); } public static class Zone { public boolean in(InputCursor c) {return true;} protected boolean inQuadrant(InputCursor c, int quad) { float x = c.getFirstEvent().getPosX()/applet.width; float y = c.getFirstEvent().getPosY()/applet.height; if (quad == 1 && y<0.5 && x < 0.5) return true; if (quad == 2 && y<0.5 && x >= 0.5) return true; if (quad == 3 && y>=0.5 && x >= 0.5) return true; if (quad == 4 && y>=0.5 && x < 0.5) return true; return false; } } public HashMap<String, ArrayList<SynthInfo>> map; public HashMap<String, HashMap<InputCursor, Quality>> qualities; public HashMap<InputCursor, KalmanFilter> filters; public static PApplet applet; public GestureEngine(PApplet app, InputDelegate in) { applet = app; map = new HashMap<String, ArrayList<SynthInfo>>(); qualities = new HashMap<String, HashMap<InputCursor,Quality>>(); qualities.put(Curvature.name, new HashMap<InputCursor,Quality>()) ; qualities.put(Velocity.name, new HashMap<InputCursor,Quality>()) ; filters = new HashMap<InputCursor, KalmanFilter>(); setupCursorListener(in); } public void addToMap(String quality, Synth synth, String param, ParamMap pMap) { addToMap(quality,synth,param,pMap, new Zone()); } public void addToMap(String quality, Synth synth, String param, ParamMap pMap, Zone z) { if (!map.containsKey(quality)) map.put(quality, new ArrayList<SynthInfo>()); map.get(quality).add(new SynthInfo(synth,param,pMap,z)); } public void setupCursorListener(final InputDelegate in) { in.addInputListener(new IMTInputEventListener() { @Override public boolean processInputEvent(MTInputEvent inEvt){ if(inEvt instanceof AbstractCursorInputEvt){ AbstractCursorInputEvt posEvt = (AbstractCursorInputEvt)inEvt; if (posEvt.hasTarget()){ if (posEvt.getId() == AbstractCursorInputEvt.INPUT_ENDED) { System.out.println("Input Ended!"); removeCursor(posEvt.getCursor()); } else if (posEvt.getId() == AbstractCursorInputEvt.INPUT_DETECTED) { System.out.println("Input Detected!"); addCursor(posEvt.getCursor()); } else { InputCursor m = posEvt.getCursor(); updateEngine(filter(m)); } } } return false; } }); } public void removeCursor(InputCursor in) { filters.remove(in); } public void addCursor(InputCursor in) { addQualitiesForCursor(in); KalmanFilter f = KalmanFilter.buildKF2D(9, 1, 20); //magicparams, still don't know what they mean. f.setX(new Matrix(new double[][]{{in.getCurrentEvtPosX()}, {in.getCurrentEvtPosY()}, {0.01}, {0.01} })); f.predict(); filters.put(in, f); } public void addQualitiesForCursor(InputCursor in) { qualities.get(Curvature.name).put(in, new Curvature(this)); qualities.get(Velocity.name).put(in, new Velocity(this)); } public InputCursor filter(InputCursor in) { AbstractCursorInputEvt evt = in.getCurrentEvent(); if (evt == null) return in; KalmanFilter f = filters.get(in); if (f == null) return in; f.correct(new Matrix(new double[][]{{evt.getPosX(), evt.getPosY()}}).transpose()); f.predict(); evt.setPositionX((float) f.getX().get(0,0)); //I get it! evt.setPositionY((float) f.getX().get(1,0)); return in; } public void updateEngine(InputCursor in) { for (HashMap<InputCursor,Quality> cursorAndQualities : qualities.values()) { if (cursorAndQualities.containsKey(in)) cursorAndQualities.get(in).update(in); } } public float getCurrentValue(String name) { for (Quality qual : qualities.get(name).values()) { return qual.getCurrentValue(); } return Float.NaN; } public float getCurrentValue(String name, InputCursor cursor) { if (qualities.get(name).containsKey(cursor)) { return qualities.get(name).get(cursor).getCurrentValue(); } return Float.NaN; } public void gestureQualityChange(String quality, float val, InputCursor in) { for (SynthInfo info : map.get(quality) ) { if (info.zone.in(in)) { try { info.synth.set(info.parameter, info.pMap.map(val)); } catch (IOException e) { /** * oops. not our problem. */ e.printStackTrace(); } } } } }