Mercurial > hg > cmdp
view src/uk/ac/qmul/eecs/depic/daw/gui/SequenceGraph.java @ 2:c0412c81d274
Added documentation
author | Fiore Martin <f.martin@qmul.ac.uk> |
---|---|
date | Thu, 18 Feb 2016 18:35:26 +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.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.Line2D; import java.util.ArrayList; import java.util.Collection; import java.util.List; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import uk.ac.qmul.eecs.depic.patterns.MathUtils; import uk.ac.qmul.eecs.depic.patterns.Range; import uk.ac.qmul.eecs.depic.patterns.Sequence; import uk.ac.qmul.eecs.depic.patterns.SequenceEvent; import uk.ac.qmul.eecs.depic.patterns.SequenceListener; /** * * A visual representation of a sequence graph. Here used for automation graphs overlaying the audio track. * * It listen to sequences (automations) and, when the sequence is updated, it updates itself * and triggers the registered change listeners. */ public class SequenceGraph implements SequenceListener { private static final float SEQUENCE_LINE_WIDTH = 4.0f; private static final int SEQUENCE_POINTS_CAPACITY = 30; private Sequence sequence; private Color color; private List<SequencePoint> sequencePoints; private List<Shape> sequenceLines; private Dimension size; private Collection<ChangeListener> listeners; private float millisecPerPixel; public SequenceGraph(Color color, Dimension size) { sequencePoints = new ArrayList<>(SEQUENCE_POINTS_CAPACITY); sequenceLines = new ArrayList<>(SEQUENCE_POINTS_CAPACITY-1); listeners = new ArrayList<>(); this.color = color; this.size = size; } public Dimension getSize(){ return size; } public void setSize(Dimension size){ this.size = size; } public void setColor(Color c){ this.color = c; } public float getMillisecondsPerPixel(){ return millisecPerPixel; } public void setMillisecPerPixel(float millisecPerPixel){ this.millisecPerPixel = millisecPerPixel; } public Color getColor(){ return color; } public List<SequencePoint> getSequencePoints(){ return sequencePoints; } public List<Shape> getSequenceLines(){ return sequenceLines; } public void draw(Graphics g ){ if(sequenceLines.isEmpty()){ return; } Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Color oldColor = g2.getColor(); g2.setColor(color); /* draw sequence points and lines */ for(Shape point : sequencePoints){ g2.fill(point); } for(Shape line : sequenceLines){ g2.fill(line); } /* restores previous state */ g2.setColor(oldColor); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); } public void addChangeListener(ChangeListener l){ listeners.add(l); } public void removeChangeListener(ChangeListener l){ listeners.remove(l); } protected void fireChangeListeners(){ ChangeEvent evt = new ChangeEvent(this); for(ChangeListener l : listeners){ l.stateChanged(evt); } } @Override public <T extends SequenceEvent> void sequenceUpdated(T evt) { updateSequence(); } /** * * Sets a new sequence to be listened to. If the argument is {@code null} * it stops listening to the previously set sequence, if any,m and clears all * sequence points and lines. it calls {@code updateSequence}. * * @param s the new sequence. */ public void setSequence(Sequence s){ /* remove this from the old sequence if any and add this to the new sequence */ if(sequence != null){ sequence.removeSequenceListener(this); } if(s != null){ s.addSequenceListener(this); } sequence = s; updateSequence(); } public Sequence getSequence(){ return sequence; } /** * Recompute the sequence points and lines. It should be called whenever a change is done to this * object. For example after {@code setSize} and {@code setMilliseconsPerPixel}. * */ public void updateSequence(){ /* clear previous values. will rebuild them if type is not NONE */ sequencePoints.clear(); sequenceLines.clear(); if(sequence == null){ /* no sequence represented any more. Fire listeners with * * sequencePoints and sequenceLines cleared */ fireChangeListeners(); return; } /* height and width of this panel in order to draw proportionately */ int height = getSize().height; int width = getSize().width; /* get the sequence range */ MathUtils.Scale scale = new MathUtils.Scale(sequence.getRange(),new Range<Float>(0.0f,(float)height)); BasicStroke stroke = new BasicStroke(SEQUENCE_LINE_WIDTH); /* if there are no sequence values, draw a straight line from the beginning to the end of the track */ if(sequence.getValuesNum() == 0){ sequenceLines.add(stroke.createStrokedShape( new Line2D.Float( 0f, height - scale.linear(sequence.getBegin()),/* height is reversed as in swing the top is 0 */ width, height - scale.linear(sequence.getEnd()))/* height is reversed as in swing the top is 0 */ )); }else{ Sequence.Value previous = null; /*previousX and previousY are the centre of sequence values calculated in the previous cycle */ int previousX = 0; int previousY = 0; for(int i=0; i<sequence.getValuesNum();i++){ Sequence.Value current = sequence.getValueAt(i); int currentX = (int)(current.getTimePosition()/millisecPerPixel) - (SequencePoint.SIZE/2) ; /* height is reversed as in swing the top is 0 */ int currentY = height - (int)(scale.linear(current.getValue())) - (SequencePoint.SIZE/2) ; /* add the point */ sequencePoints.add(new SequencePoint(current,currentX,currentY)); if(previous == null){ /* add the line from the beginning to the first point */ sequenceLines.add(stroke.createStrokedShape( new Line2D.Float( 0f, height - scale.linear(sequence.getBegin()), /* height is reversed as in swing the top is 0 */ currentX + (SequencePoint.SIZE/2), currentY + (SequencePoint.SIZE/2) ))); }else{ /* add the line with previous point */ sequenceLines.add(stroke.createStrokedShape( new Line2D.Float( previousX + (SequencePoint.SIZE/2), previousY + (SequencePoint.SIZE/2), currentX + (SequencePoint.SIZE/2), currentY + (SequencePoint.SIZE/2) ))); } /* prepare for next cycle */ previous = current; previousX = currentX; previousY = currentY; } /* add the line from the last point to the end */ sequenceLines.add(stroke.createStrokedShape( new Line2D.Float( previousX + (SequencePoint.SIZE/2), previousY + (SequencePoint.SIZE/2), width, height - scale.linear(sequence.getEnd()) ))); /* height is reversed as in swing the top is 0 */ } fireChangeListeners(); } }