Mercurial > hg > cmdp
view src/uk/ac/qmul/eecs/depic/daw/gui/AudioTrackInput.java @ 4:473da40f3d39 tip
added html formatting to Daw/package-info.java
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Thu, 25 Feb 2016 17:50:09 +0000 |
parents | 3074a84ef81e |
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.Dimension; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JComponent; import javax.swing.JPopupMenu; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.MouseInputAdapter; 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.Direction; import uk.ac.qmul.eecs.depic.daw.Selection; import uk.ac.qmul.eecs.depic.daw.SoundType; import uk.ac.qmul.eecs.depic.daw.SoundWave; /* * This class handles the interaction with the mouse. When the mouse is dragged around * the corresponding selection appears on this audio track. When the user releases the * mouse button (and therefore stops dragging) the selection is assigned to * the underlying sound wave by a call to setSelection(). */ final class AudioTrackInput extends MouseInputAdapter implements KeyListener { private static final int KEY_SELECTION_INCREMENT = 1; private static final int KEY_PRESS_NO_SELECTION = -1; private int mousePress; // where the user presses the mouse private int keyPress; /* used to check whether the user is dragging a new selection */ private boolean isDragging; private Selection mouseSelection; private AudioTrack track; private SequencePoint dragAutomationPoint; AudioTrackInput(AudioTrack track){ this.track = track; setAudioTrackActions(track); mouseSelection = Selection.ZERO_SELECTION; keyPress = KEY_PRESS_NO_SELECTION; } private void setAudioTrackActions(AudioTrack track){ for(Action action : AutomationGraphActions.getInstance()){ Object unique = new Object(); track.getInputMap(JComponent.WHEN_FOCUSED).put((KeyStroke)action.getValue(Action.ACCELERATOR_KEY), unique); track.getActionMap().put(unique,action); } } /** * Sets the mouse selection to a new {@code Selection} ranging from {@code start} to {@code end}. * * @param start * @param end */ public void setMouseSelection(int start, int end){ Selection oldSelection = mouseSelection; if(end == -1) // open Selection mouseSelection = new Selection(start,track.getScaleFactor()); else mouseSelection = new Selection(start,end,track.getScaleFactor()); track.firePropertyChange("mouseDragSelection", oldSelection, mouseSelection); track.repaint(); } /** * Sets the mouse selection and sets the beginning of the selection action to * either {@code start} or {@start end} (according to the value of {@code mouseAtStart}). * * The beginning of the selection action is where a user presses the mouse click * in order to start a dragging action which creates the selections. The click * point affects how the selection is: if the user drags towards * left, the selection starts from the current dragging point and ends at the click point; * conversely if the user drags towards right the selection starts at the click point and * end at the current dragging point. The current dragging point changes as the user drags the mouse * around * * @param start the beginning of the selection * @param end start the end of the selection * @param mouseAtStart {@code true} if the beginning of the selection action was the * selection start, {@code false} if it's the selection end * */ private void setMouseSelection(int start, int end, boolean mouseAtStart){ // FIXME remove if(end == -1){ // open selection mousePress = start; }else{ mousePress = mouseAtStart ? start : end; } setMouseSelection(start,end); } /** * Returns the current mouse selection or {@code Selection.VOID_SELECTION} if there is no selection. * * @return the current mouse selection or {@code Selection.VOID_SELECTION} if there is no selection. */ public Selection getMouseSelection(){ return mouseSelection; } @Override public void mousePressed(MouseEvent evt){ if(SwingUtilities.isLeftMouseButton(evt)){ /* if automation is on */ if(track.getSoundWave().hasSequence()){ for(SequencePoint p : track.getAutomationGraph().getSequencePoints()){ if(p.contains(evt.getX(),evt.getY())){ dragAutomationPoint = p; return; } } } /* keep track of where the mouse is pressed. If the user drags a selection this is needed * * to make the right selection according to the direction of the dragging. see mouseDragged() */ mousePress = evt.getX(); } } @Override public void mouseDragged(MouseEvent evt){ if(SwingUtilities.isLeftMouseButton(evt)){ /* bound the dragging to the space within the sound wave in the track */ Dimension trackSize = track.getSize(); if(evt.getX()<0 || evt.getX() >= track.getSoundWave().getChunkNum() || evt.getY()<0 || evt.getY() > trackSize.height ) return; /* if we are dragging an automation point, change its coordinate and refresh the automation */ if(track.getSoundWave().hasSequence() && dragAutomationPoint != null){ Point2D.Float pointCoord = AudioTrack.getAutomationCoord(track,evt.getX(),evt.getY()); AutomationValue automVal = (AutomationValue)dragAutomationPoint.getSequenceValue(); automVal.setLocation(pointCoord.x,pointCoord.y); return; } /* update the mouse selection as the user drags the mouse. The selection differs * * according to the direction of the dragging. If the user moves right from where * * they pressed, then the start of the selection is the press point, and the end the * * current dragging point (evt.getX()). Conversely, if the user moves to the left, * * the start of the selection is the dragging point and the end is the press point. * * The press point is the value assigned to mousePressX in mousePressed */ isDragging = true; if(evt.getX() > mousePress){ setMouseSelection(mousePress,evt.getX()); }else{ setMouseSelection(evt.getX(),mousePress); } } } @Override public void mouseReleased(MouseEvent evt){ if(SwingUtilities.isLeftMouseButton(evt)){ dragAutomationPoint = null; if(isDragging){ /* if the user was dragging update the selection in the sound wave */ track.getSoundWave().setSelection(getMouseSelection()); isDragging = false; } } } @Override public void mouseClicked(MouseEvent evt){ /* left click : set the cursor position * * right click : open pop up menu */ if(SwingUtilities.isLeftMouseButton(evt)){ track.getSoundWave().setPosition(evt.getX()); }else if(SwingUtilities.isRightMouseButton(evt)){ /* show automation popup only if an automation is is displayed */ if(track.getSoundWave().hasSequence()){ for(Shape point : track.getAutomationGraph().getSequencePoints()){ if(point.contains(evt.getX(), evt.getY())){ showAutomationPopup(point,evt.getX(),evt.getY()); return; } } for(Shape line : track.getAutomationGraph().getSequenceLines()){ if(line.contains(evt.getX(), evt.getY())){ showAutomationPopup(line,evt.getX(),evt.getY()); return; } } } /* gets here only if no automation has been found under the mouse pointer */ if(track.getSoundWave().getDbWave().hasSequence()){ showPeakLevelPopup(evt.getX(),evt.getY()); } } } void showAutomationPopup(final Shape selectedShape, final int x, final int y){ final SoundWave wave = track.getSoundWave(); if(x >= wave.getChunkNum()){ /* don't show if the user clicks past the sound wave */ return; } JPopupMenu pop = new JPopupMenu(); if(selectedShape instanceof SequencePoint){ pop.add(new AbstractAction("Remove automation point"){ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { SequencePoint point = (SequencePoint)selectedShape; Automation automation = wave.getParametersControl().getCurrentAutomation(); automation.remove((AutomationValue)point.getSequenceValue()); } }); }else{ // Line2D pop.add(new AbstractAction("Add automation point"){ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { Automation automation = wave.getParametersControl().getCurrentAutomation(); Point2D.Float p = AudioTrack.getAutomationCoord(track,x,y); automation.add(p.x, p.y); } }); } pop.add(new AbstractAction("Reset Automation"){ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent evt) { AutomationGraphActions.getInstance().resetAutomationAction.handleActionPerformed(track); } }); Action listenToAutomation = new AbstractAction("Listen to Automation"){ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { AutomationGraphActions.getInstance().listenWholeAutomationAction.handleActionPerformed(track); } }; listenToAutomation.putValue(Action.ACCELERATOR_KEY, AutomationGraphActions.getInstance().listenWholeAutomationAction.getValue(Action.ACCELERATOR_KEY)); pop.add(listenToAutomation); Action stopListenToAutomation = new AbstractAction("Stop Listen To Automation"){ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { AutomationGraphActions.getInstance().stopListenWholeAutomationAction.handleActionPerformed(track); } }; stopListenToAutomation.putValue(Action.ACCELERATOR_KEY, AutomationGraphActions.getInstance().stopListenWholeAutomationAction.getValue(Action.ACCELERATOR_KEY)); pop.add(stopListenToAutomation); Action switchListenAutomation = new AbstractAction("Switch Listen Automation "+ (AutomationGraphActions.getInstance().switchListenAutomationAction.isSwitchOn() ? "off" : "on")){ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { AutomationGraphActions.getInstance().switchListenAutomationAction.handleActionPerformed(track); } }; switchListenAutomation.putValue(Action.ACCELERATOR_KEY, AutomationGraphActions.getInstance().switchListenAutomationAction.getValue(Action.ACCELERATOR_KEY)); pop.add(switchListenAutomation); pop.show(track, x, y); } void showPeakLevelPopup(int x , int y){ final SoundWave wave = track.getSoundWave(); if(x >= wave.getChunkNum()){ /* don't show if the user clicks past the sound wave */ return; } JPopupMenu pop = new JPopupMenu(); pop.add(new AbstractAction("Switch listen peak level " + (AutomationGraphActions.getInstance().switchListenPeakLevelAction.isSwitchOn() ? "off" : "on")){ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent evt) { AutomationGraphActions.getInstance().switchListenPeakLevelAction.handleActionPerformed(track); } }); pop.add(new AbstractAction("Listen to Peak Level"){ private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent evt) { AutomationGraphActions.getInstance().listenWholePeakLevelAction.handleActionPerformed(track); } }); pop.show(track,x,y); } @Override public void keyPressed(KeyEvent evt) { /* adjust the selection */ if(evt.isShiftDown() && evt.isControlDown() && !mouseSelection.equals(Selection.ZERO_SELECTION )){ Direction d = Direction.NONE; if(evt.getKeyCode() == KeyEvent.VK_LEFT){ d = Direction.LEFT; }else if(evt.getKeyCode() == KeyEvent.VK_RIGHT){ d = Direction.RIGHT; } else { return; } SoundWave wave = track.getSoundWave(); int cursorPos = wave.getCurrentChunkPosition(); if(cursorPos == mouseSelection.getStart()){ if(d == Direction.LEFT){ // left wave.setSelection(new Selection( mouseSelection.getStart()-KEY_SELECTION_INCREMENT, mouseSelection.getEnd(), wave.getScaleFactor())); wave.scan(cursorPos-KEY_SELECTION_INCREMENT); }else{ // right if(mouseSelection.getEnd() - mouseSelection.getStart() <= KEY_SELECTION_INCREMENT){ /* cannot push it too right so as to trespass the end of the selection */ Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR); }else{ wave.setSelection(new Selection( mouseSelection.getStart()+KEY_SELECTION_INCREMENT, mouseSelection.getEnd(), wave.getScaleFactor())); wave.scan(cursorPos+KEY_SELECTION_INCREMENT); } } }else if (cursorPos == mouseSelection.getEnd()){ if(d == Direction.LEFT){ // left if(mouseSelection.getEnd() - mouseSelection.getStart() <= KEY_SELECTION_INCREMENT){ /* cannot push it too keft so as to trespass the start of the selection */ Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR); }else{ wave.setSelection(new Selection( mouseSelection.getStart(), mouseSelection.getEnd()-KEY_SELECTION_INCREMENT, wave.getScaleFactor())); wave.scan(cursorPos-KEY_SELECTION_INCREMENT); } }else{ // right wave.setSelection(new Selection( mouseSelection.getStart(), mouseSelection.getEnd()+KEY_SELECTION_INCREMENT, wave.getScaleFactor())); wave.scan(cursorPos+KEY_SELECTION_INCREMENT); } } /* make selection with right arrow key */ } else if(evt.getKeyCode() == KeyEvent.VK_RIGHT && evt.isShiftDown() ){ if(keyPress == KEY_PRESS_NO_SELECTION){ // new selection keyPress = track.getSoundWave().getCurrentChunkPosition(); setMouseSelection(keyPress, keyPress+KEY_SELECTION_INCREMENT); /* scrub along with the selection right end */ track.getSoundWave().scan(getMouseSelection().getEnd()); /* start the selection sound */ }else{ // user is adjusting the selection /* if mouseSelection.getStart == keypress, it means the selection is at the * * right side of where the user started to select. Conversely id mouse selection * * .getEnd == keypress, the selection is at the left of the point where the user * * started to select. In the former case pressing the right key will expand * * the selection to the right, whereas in latter case pressing the right key will * * shrink the selection to the right. */ if(mouseSelection.getStart() == keyPress){ setMouseSelection(keyPress,mouseSelection.getEnd()+KEY_SELECTION_INCREMENT); track.getSoundWave().scan(getMouseSelection().getEnd()); }else{ // getEnd == keyPress setMouseSelection(mouseSelection.getStart()+KEY_SELECTION_INCREMENT,keyPress); track.getSoundWave().scan(getMouseSelection().getStart()); } } /* make selection with left arrow key */ }else if(evt.getKeyCode() == KeyEvent.VK_LEFT && evt.isShiftDown()){ if(keyPress == KEY_PRESS_NO_SELECTION){ keyPress = track.getSoundWave().getCurrentChunkPosition(); /* keep the selection after the beginning - 0 - of the audio track */ int start = keyPress-KEY_SELECTION_INCREMENT > 0 ? keyPress-KEY_SELECTION_INCREMENT : 0; setMouseSelection(start,keyPress); /* scrub along with the selection left end */ track.getSoundWave().scan(getMouseSelection().getStart()); }else{ // user is adjusting the selection /* See selectio adjustment for the right key. * * The idea is the same, just with inverted left and right */ if(mouseSelection.getEnd() == keyPress){ setMouseSelection(mouseSelection.getStart()-KEY_SELECTION_INCREMENT,keyPress); track.getSoundWave().scan(getMouseSelection().getStart()); }else{ // getStart == keyPress setMouseSelection(keyPress,mouseSelection.getEnd()-KEY_SELECTION_INCREMENT); track.getSoundWave().scan(getMouseSelection().getEnd()); } } /* jump to beginning of selection, if any */ }else if(evt.getKeyCode() == KeyEvent.VK_F2 || evt.getKeyCode() == KeyEvent.VK_F3){ if(mouseSelection.equals(Selection.ZERO_SELECTION)){ Daw.getSoundEngineFactory().getSharedSonification().play(SoundType.ERROR); }else if (evt.getKeyCode() == KeyEvent.VK_F2){ track.getSoundWave().setPosition(mouseSelection.getStart()); }else{ track.getSoundWave().setPosition(mouseSelection.getEnd()); } } } @Override public void keyReleased(KeyEvent evt) { /* create selection in the model when user releases shift key */ if(evt.getKeyCode() == KeyEvent.VK_SHIFT && keyPress != KEY_PRESS_NO_SELECTION){ keyPress = KEY_PRESS_NO_SELECTION; track.getSoundWave().setSelection(getMouseSelection()); } } @Override public void keyTyped(KeyEvent evt) { } } // class AudioTrackMouseInteraction