Mercurial > hg > cmdp
view src/uk/ac/qmul/eecs/depic/daw/gui/AutomationGraphActions.java @ 0:3074a84ef81e
first import
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Wed, 26 Aug 2015 16:16:53 +0100 |
parents | |
children |
line wrap: on
line source
/* Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation. Copyright (C) 2015 Queen Mary University of London (http://depic.eecs.qmul.ac.uk/) 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 3 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, see <http://www.gnu.org/licenses/>. */ package uk.ac.qmul.eecs.depic.daw.gui; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.geom.Point2D; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.prefs.Preferences; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JComponent; import javax.swing.JOptionPane; import javax.swing.KeyStroke; import uk.ac.qmul.eecs.depic.daw.Automation; import uk.ac.qmul.eecs.depic.daw.AutomationValue; import uk.ac.qmul.eecs.depic.daw.Daw; import uk.ac.qmul.eecs.depic.daw.Sonification; import uk.ac.qmul.eecs.depic.daw.SoundType; import uk.ac.qmul.eecs.depic.daw.SoundWave; import uk.ac.qmul.eecs.depic.daw.beads.sonification.BeadsSonification; import uk.ac.qmul.eecs.depic.patterns.Range; import uk.ac.qmul.eecs.depic.patterns.Sequence; /** * * Actions for the manipulation of the automation graph. * * */ class AutomationGraphActions implements Iterable<Action> { private static AutomationGraphActions singleton; private SequencePoint heldPoint; public final AddAutomationValueAction addAutomationValueAction; public final RemoveAutomationValueAction removeAutomationValueAction; public final MoveAutomationValueAction moveAutomationValueUpAction; public final MoveAutomationValueAction moveAutomationValueLeftAction; public final MoveAutomationValueAction moveAutomationValueDownAction; public final MoveAutomationValueAction moveAutomationValueRightAction; public final ResetAutomationValuesAction resetAutomationAction; public final ListenWholeAutomationAction listenWholeAutomationAction; public final StopListenWholeAutomationAction stopListenWholeAutomationAction; public final SwitchListenAutomationAction switchListenAutomationAction; public final SwitchListenPeakLevelAction switchListenPeakLevelAction; public final ListenWholePeakLevelAction listenWholePeakLevelAction; private Sonification sonification; static AutomationGraphActions getInstance(){ if(singleton == null) singleton = new AutomationGraphActions(); return singleton; } private AutomationGraphActions (){ addAutomationValueAction = new AddAutomationValueAction(); removeAutomationValueAction = new RemoveAutomationValueAction(); moveAutomationValueUpAction = new MoveAutomationValueAction(KeyStroke.getKeyStroke("ctrl UP"),0.0f,0.05f); moveAutomationValueDownAction = new MoveAutomationValueAction(KeyStroke.getKeyStroke("ctrl DOWN"),0.0f,-0.05f); moveAutomationValueLeftAction = new MoveAutomationValueAction(KeyStroke.getKeyStroke("ctrl LEFT"),-50.0f,0.0f); moveAutomationValueRightAction = new MoveAutomationValueAction(KeyStroke.getKeyStroke("ctrl RIGHT"),50.0f,0.0f); resetAutomationAction = new ResetAutomationValuesAction(); listenWholeAutomationAction = new ListenWholeAutomationAction(); stopListenWholeAutomationAction = new StopListenWholeAutomationAction(); switchListenAutomationAction = new SwitchListenAutomationAction(); switchListenPeakLevelAction = new SwitchListenPeakLevelAction(); listenWholePeakLevelAction = new ListenWholePeakLevelAction(); sonification = Daw.getSoundEngineFactory().getSharedSonification(); } /** * Create a new Actions than plays the automation value sound when it is at zero point, * that is in the middle of the automation range * * @param val * @return */ private static Action createZeroValAction(final Sequence.Value val){ return new AbstractAction() { private static final long serialVersionUID = 1L; private boolean doneOnce = false; @Override public void actionPerformed(ActionEvent evt){ /* only play feedback once. This action remains until a new action * for the same keystroke is installed. A new action is installed * when the user presses ctrl + up/down/left/right arrow (see MoveAutomation * Feedback as to be given only once by each zeroAction, otherwise * the sonification will be played each time the user releases a ctrl + * arrow action, regardless of whether they actually moved an automation or nor */ if(doneOnce){ return; }else{ doneOnce = true; } Daw.getSoundEngineFactory().getSharedSonification().getSequenceMapping(SoundType.AUTOMATION).renderValue( new Sequence.Value() { @Override public int index() { return val.index(); } @Override public float getValue() { Range<Float> range = val.getSequence().getRange(); return range.getStart() + range.lenght()/2; } @Override public float getTimePosition() { return val.getTimePosition(); } @Override public Sequence getSequence() { return val.getSequence(); } }); } }; } @Override public Iterator<Action> iterator(){ return new Iterator<Action>(){ private int index = 0; private Action [] actions = new Action [] { addAutomationValueAction, removeAutomationValueAction, moveAutomationValueUpAction, moveAutomationValueLeftAction, moveAutomationValueDownAction, moveAutomationValueRightAction, resetAutomationAction, listenWholeAutomationAction, stopListenWholeAutomationAction, switchListenAutomationAction, switchListenPeakLevelAction, listenWholePeakLevelAction }; @Override public boolean hasNext() { return index < actions.length; } @Override public Action next() { if(index == actions.length) throw new NoSuchElementException(); return actions[index++]; } @Override public void remove() { throw new UnsupportedOperationException("remove not supported"); } }; } static abstract class AutomationGraphAction extends AbstractAction { private static final long serialVersionUID = 1L; AutomationGraphAction(KeyStroke keyStroke){ putValue(Action.ACCELERATOR_KEY,keyStroke); } @Override public void actionPerformed(ActionEvent evt){ handleActionPerformed((AudioTrack)evt.getSource()); } public abstract void handleActionPerformed(AudioTrack track); } class AddAutomationValueAction extends AutomationGraphAction { private static final long serialVersionUID = 1L; public AddAutomationValueAction() { super(KeyStroke.getKeyStroke("ctrl INSERT")); } @Override public void handleActionPerformed(AudioTrack track) { if(track == null || !track.getSoundWave().hasSequence()) { return; } SoundWave wave = track.getSoundWave(); int cursorPos = wave.getCurrentChunkPosition(); Point2D.Float p = AudioTrack.getAutomationCoord(track, cursorPos, track.getHeight()/2); /* if user is trying to create an automation outside the bounds of the automation, notify the error */ if(p == null){ sonification.play(SoundType.ERROR); return; } /* if a new point is created when moving another point the held point will still be the old one * * by setting it to null the hel point will become the new one as soon as the user will try to move it */ heldPoint = null; sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue( wave.getParametersControl().getCurrentAutomation().add(p.x, p.y)); } } // class AddAutomationAction class MoveAutomationValueAction extends AutomationGraphAction { private static final long serialVersionUID = 1L; private float deltaX; private float deltaY; public MoveAutomationValueAction(KeyStroke keyStroke, float deltaX, float deltaY){ super(keyStroke); this.deltaX = deltaX; this.deltaY = deltaY; heldPoint = null; } @Override public void handleActionPerformed(final AudioTrack track) { if(track == null || !track.getSoundWave().hasSequence()){ heldPoint = null; return; } int cursorPos = track.getSoundWave().getCurrentChunkPosition(); /* if ctrl is held (heldPoint != null) it means the user is still moving around a * * point that they grabbed previously. So the check that the point be under the * * cursor must not be done, and the point can be moved and played anyway */ if(heldPoint != null){ changeAutomation((AutomationValue)heldPoint.getSequenceValue(),track); return; } /* find the automaton value under the cursor position if any */ for(SequencePoint itr : track.getAutomationGraph().getSequencePoints()){ if( itr.isXCentredAt(cursorPos) ){ /* p is now the heldPoint, until the user releaser the ctrl key this point will be referenced * * in next move action even if it's not under the the cursor is not under the cursor anymore */ heldPoint = itr; /* set up an action that will stop scrubbing free the held point when the user releases the ctrl key */ track.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, 0 , true), "heldPoint"); track.getActionMap().put("heldPoint", new AbstractAction(){ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent evt) { heldPoint = null; /* stop scrubbing */ track.getSoundWave().scan(SoundWave.STOP_SCANNING); /* de-register itself from the track registered actions */ track.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, 0 , true), "none"); track.getActionMap().remove("heldPoint"); } }); changeAutomation((AutomationValue)itr.getSequenceValue(),track); break; } } } protected void changeAutomation(AutomationValue val, AudioTrack track){ val.setLocation(val.getTimePosition()+deltaX, val.getValue()+deltaY); /* install the action that plays the zero value when the key is released */ KeyStroke thisActionKeyStroke = (KeyStroke)getValue(Action.ACCELERATOR_KEY); KeyStroke zeroValKeyStroke = KeyStroke.getKeyStroke(thisActionKeyStroke.getKeyCode(),thisActionKeyStroke.getModifiers(),true); if(Preferences.userNodeForPackage(BeadsSonification.class).getBoolean("render_val.ref", false)){ track.getInputMap(JComponent.WHEN_FOCUSED).put(zeroValKeyStroke, "zeroVal"); track.getActionMap().put("zeroVal", createZeroValAction(val)); } /* besides moving the automation, scrub the cursor along */ int scanTo = (int)(val.getTimePosition() / track.getSoundWave().getMillisecPerChunk()); track.getSoundWave().scan(scanTo); sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue(heldPoint.getSequenceValue()); } } // class MoveAutomationValueAction class RemoveAutomationValueAction extends AutomationGraphAction { private static final long serialVersionUID = 1L; public RemoveAutomationValueAction() { super(KeyStroke.getKeyStroke("ctrl DELETE")); } @Override public void handleActionPerformed(AudioTrack track) { if(track == null || !track.getSoundWave().hasSequence()){ return; } int cursorPos = track.getSoundWave().getCurrentChunkPosition(); /* find the automaton value */ SequencePoint p = null; for(SequencePoint itr : track.getAutomationGraph().getSequencePoints()){ float dist = Math.abs(cursorPos - (int)itr.getCenterX() ); if( dist < SequencePoint.SIZE ){ if(p == null || dist < Math.abs((int)p.getCenterX() - (int)itr.getCenterX())){ p = itr; /* don't break as it keeps looking for other points, which might be closer to the cursor */ } } } /* if found, then remove it and play it */ if(p != null){ ((Automation)p.getSequenceValue().getSequence()).remove((AutomationValue)p.getSequenceValue()); sonification.getSequenceMapping(SoundType.AUTOMATION).renderValue(p.getSequenceValue()); } } } // class RemoveAutomationAction class ResetAutomationValuesAction extends AutomationGraphAction { private static final long serialVersionUID = 1L; public ResetAutomationValuesAction() { super(KeyStroke.getKeyStroke("ctrl R ")); } @Override public void handleActionPerformed(AudioTrack track){ if(track == null || !track.getSoundWave().hasSequence()){ return; } int confirmation = JOptionPane.showConfirmDialog(track, "Are you sure you want to reset the automation ?", // FIXME bundle "Confirmation Dialog", JOptionPane.YES_NO_OPTION ); if(confirmation == JOptionPane.YES_OPTION){ track.getSoundWave().getParametersControl().getCurrentAutomation().reset(); } } } // class ClearAutomationValuesAction class ListenWholeAutomationAction extends AutomationGraphAction { private static final long serialVersionUID = 1L; public ListenWholeAutomationAction() { super(KeyStroke.getKeyStroke("ctrl shift L")); } @Override public void handleActionPerformed(AudioTrack track){ if(track == null || (!track.getSoundWave().hasSequence()) ){ sonification.play(SoundType.ERROR); return; } track.getSoundWave().getTransportControl().rew(); sonification.getSequenceMapping(SoundType.AUTOMATION).renderCurve( track.getSoundWave().getParametersControl().getCurrentAutomation(),0.0f); track.getSoundWave().getTransportControl().play(); } } // class SonifyAutomationAction class StopListenWholeAutomationAction extends AutomationGraphAction { private static final long serialVersionUID = 1L; public StopListenWholeAutomationAction(){ super(KeyStroke.getKeyStroke("ctrl K")); } @Override public void handleActionPerformed(AudioTrack track){ sonification.getSequenceMapping(SoundType.AUTOMATION).renderCurve( null,-100.0f); sonification.getSequenceMapping(SoundType.PEAK_LEVEL).renderCurve( null,-100.0f); track.soundWave.getTransportControl().stop(); } } class SwitchListenAutomationAction extends AutomationGraphAction { private static final long serialVersionUID = 1L; private boolean isOn; public SwitchListenAutomationAction(){ super(KeyStroke.getKeyStroke("ctrl L")); isOn = false; } @Override public void handleActionPerformed(AudioTrack track){ if(track.getSoundWave().hasSequence()){ isOn = !isOn; track.showAutomationSound(isOn); sonification.play(SoundType.OK); }else{ sonification.play(SoundType.ERROR); } } public boolean isSwitchOn(){ return isOn; } } // class SwitchListenAutomation class SwitchListenPeakLevelAction extends AutomationGraphAction { private static final long serialVersionUID = 1L; private boolean isOn; SwitchListenPeakLevelAction(){ super(KeyStroke.getKeyStroke("ctrl P")); isOn = false; } @Override public void handleActionPerformed(AudioTrack track){ if( track == null || (!track.getSoundWave().getDbWave().hasSequence())){ Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR); return; } isOn = !isOn; track.showPeakLevelSound(isOn); Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.OK); } public boolean isSwitchOn(){ return isOn; } } // SwitchListenPeakLevelAction class ListenWholePeakLevelAction extends AutomationGraphAction { private static final long serialVersionUID = 1L; ListenWholePeakLevelAction(){ super(KeyStroke.getKeyStroke("ctrl shift P")); } @Override public void handleActionPerformed(AudioTrack track){ if(track == null || (!track.getSoundWave().getDbWave().hasSequence()) ){ sonification.play(SoundType.ERROR); return; } track.getSoundWave().getTransportControl().rew(); sonification.getSequenceMapping(SoundType.PEAK_LEVEL).renderCurve( track.getSoundWave().getDbWave().getSequence(),0.0f); track.getSoundWave().getTransportControl().play(); } } }