annotate src/uk/ac/qmul/eecs/depic/daw/gui/SequenceGraph.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 c0412c81d274
children
rev   line source
f@0 1 /*
f@0 2 Cross-Modal DAW Prototype - Prototype of a simple Cross-Modal Digital Audio Workstation.
f@0 3
f@0 4 Copyright (C) 2015 Queen Mary University of London (http://depic.eecs.qmul.ac.uk/)
f@0 5
f@0 6 This program is free software: you can redistribute it and/or modify
f@0 7 it under the terms of the GNU General Public License as published by
f@0 8 the Free Software Foundation, either version 3 of the License, or
f@0 9 (at your option) any later version.
f@0 10
f@0 11 This program is distributed in the hope that it will be useful,
f@0 12 but WITHOUT ANY WARRANTY; without even the implied warranty of
f@0 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
f@0 14 GNU General Public License for more details.
f@0 15
f@0 16 You should have received a copy of the GNU General Public License
f@0 17 along with this program. If not, see <http://www.gnu.org/licenses/>.
f@0 18 */
f@0 19 package uk.ac.qmul.eecs.depic.daw.gui;
f@0 20
f@0 21 import java.awt.BasicStroke;
f@0 22 import java.awt.Color;
f@0 23 import java.awt.Dimension;
f@0 24 import java.awt.Graphics;
f@0 25 import java.awt.Graphics2D;
f@0 26 import java.awt.RenderingHints;
f@0 27 import java.awt.Shape;
f@0 28 import java.awt.geom.Line2D;
f@0 29 import java.util.ArrayList;
f@0 30 import java.util.Collection;
f@0 31 import java.util.List;
f@0 32
f@0 33 import javax.swing.event.ChangeEvent;
f@0 34 import javax.swing.event.ChangeListener;
f@0 35
f@0 36 import uk.ac.qmul.eecs.depic.patterns.MathUtils;
f@0 37 import uk.ac.qmul.eecs.depic.patterns.Range;
f@0 38 import uk.ac.qmul.eecs.depic.patterns.Sequence;
f@0 39 import uk.ac.qmul.eecs.depic.patterns.SequenceEvent;
f@0 40 import uk.ac.qmul.eecs.depic.patterns.SequenceListener;
f@0 41
f@2 42 /**
f@2 43 *
f@2 44 * A visual representation of a sequence graph. Here used for automation graphs overlaying the audio track.
f@2 45 *
f@2 46 * It listen to sequences (automations) and, when the sequence is updated, it updates itself
f@2 47 * and triggers the registered change listeners.
f@2 48 */
f@2 49 public class SequenceGraph implements SequenceListener {
f@0 50 private static final float SEQUENCE_LINE_WIDTH = 4.0f;
f@0 51 private static final int SEQUENCE_POINTS_CAPACITY = 30;
f@0 52
f@0 53 private Sequence sequence;
f@0 54 private Color color;
f@0 55 private List<SequencePoint> sequencePoints;
f@0 56 private List<Shape> sequenceLines;
f@0 57 private Dimension size;
f@0 58 private Collection<ChangeListener> listeners;
f@0 59 private float millisecPerPixel;
f@0 60
f@0 61 public SequenceGraph(Color color, Dimension size) {
f@0 62 sequencePoints = new ArrayList<>(SEQUENCE_POINTS_CAPACITY);
f@0 63 sequenceLines = new ArrayList<>(SEQUENCE_POINTS_CAPACITY-1);
f@0 64 listeners = new ArrayList<>();
f@0 65 this.color = color;
f@0 66 this.size = size;
f@0 67 }
f@0 68
f@0 69 public Dimension getSize(){
f@0 70 return size;
f@0 71 }
f@0 72
f@0 73 public void setSize(Dimension size){
f@0 74 this.size = size;
f@0 75 }
f@0 76
f@0 77 public void setColor(Color c){
f@0 78 this.color = c;
f@0 79 }
f@0 80
f@0 81 public float getMillisecondsPerPixel(){
f@0 82 return millisecPerPixel;
f@0 83 }
f@0 84
f@0 85 public void setMillisecPerPixel(float millisecPerPixel){
f@0 86 this.millisecPerPixel = millisecPerPixel;
f@0 87 }
f@0 88
f@0 89 public Color getColor(){
f@0 90 return color;
f@0 91 }
f@0 92
f@0 93 public List<SequencePoint> getSequencePoints(){
f@0 94 return sequencePoints;
f@0 95 }
f@0 96
f@0 97 public List<Shape> getSequenceLines(){
f@0 98 return sequenceLines;
f@0 99 }
f@0 100
f@0 101 public void draw(Graphics g ){
f@0 102 if(sequenceLines.isEmpty()){
f@0 103 return;
f@0 104 }
f@0 105
f@0 106 Graphics2D g2 = (Graphics2D)g;
f@0 107 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
f@0 108 Color oldColor = g2.getColor();
f@0 109
f@0 110 g2.setColor(color);
f@0 111
f@0 112 /* draw sequence points and lines */
f@0 113 for(Shape point : sequencePoints){
f@0 114 g2.fill(point);
f@0 115 }
f@0 116
f@0 117 for(Shape line : sequenceLines){
f@0 118 g2.fill(line);
f@0 119 }
f@0 120 /* restores previous state */
f@0 121 g2.setColor(oldColor);
f@0 122 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
f@0 123 }
f@0 124
f@0 125 public void addChangeListener(ChangeListener l){
f@0 126 listeners.add(l);
f@0 127 }
f@0 128
f@0 129 public void removeChangeListener(ChangeListener l){
f@0 130 listeners.remove(l);
f@0 131 }
f@0 132
f@0 133 protected void fireChangeListeners(){
f@0 134 ChangeEvent evt = new ChangeEvent(this);
f@0 135 for(ChangeListener l : listeners){
f@0 136 l.stateChanged(evt);
f@0 137 }
f@0 138 }
f@0 139
f@0 140 @Override
f@0 141 public <T extends SequenceEvent> void sequenceUpdated(T evt) {
f@0 142 updateSequence();
f@0 143 }
f@0 144
f@0 145 /**
f@0 146 *
f@0 147 * Sets a new sequence to be listened to. If the argument is {@code null}
f@0 148 * it stops listening to the previously set sequence, if any,m and clears all
f@0 149 * sequence points and lines. it calls {@code updateSequence}.
f@0 150 *
f@0 151 * @param s the new sequence.
f@0 152 */
f@0 153 public void setSequence(Sequence s){
f@0 154 /* remove this from the old sequence if any and add this to the new sequence */
f@0 155 if(sequence != null){
f@0 156 sequence.removeSequenceListener(this);
f@0 157 }
f@0 158
f@0 159 if(s != null){
f@0 160 s.addSequenceListener(this);
f@0 161 }
f@0 162
f@0 163 sequence = s;
f@0 164 updateSequence();
f@0 165 }
f@0 166
f@0 167 public Sequence getSequence(){
f@0 168 return sequence;
f@0 169 }
f@0 170
f@0 171 /**
f@0 172 * Recompute the sequence points and lines. It should be called whenever a change is done to this
f@0 173 * object. For example after {@code setSize} and {@code setMilliseconsPerPixel}.
f@0 174 *
f@0 175 */
f@0 176 public void updateSequence(){
f@0 177 /* clear previous values. will rebuild them if type is not NONE */
f@0 178 sequencePoints.clear();
f@0 179 sequenceLines.clear();
f@0 180
f@0 181 if(sequence == null){
f@0 182 /* no sequence represented any more. Fire listeners with *
f@0 183 * sequencePoints and sequenceLines cleared */
f@0 184 fireChangeListeners();
f@0 185 return;
f@0 186 }
f@0 187
f@0 188 /* height and width of this panel in order to draw proportionately */
f@0 189 int height = getSize().height;
f@0 190 int width = getSize().width;
f@0 191
f@0 192 /* get the sequence range */
f@0 193 MathUtils.Scale scale = new MathUtils.Scale(sequence.getRange(),new Range<Float>(0.0f,(float)height));
f@0 194 BasicStroke stroke = new BasicStroke(SEQUENCE_LINE_WIDTH);
f@0 195
f@0 196 /* if there are no sequence values, draw a straight line from the beginning to the end of the track */
f@0 197 if(sequence.getValuesNum() == 0){
f@0 198 sequenceLines.add(stroke.createStrokedShape(
f@0 199 new Line2D.Float(
f@0 200 0f,
f@0 201 height - scale.linear(sequence.getBegin()),/* height is reversed as in swing the top is 0 */
f@0 202 width,
f@0 203 height - scale.linear(sequence.getEnd()))/* height is reversed as in swing the top is 0 */
f@0 204 ));
f@0 205 }else{
f@0 206 Sequence.Value previous = null;
f@0 207 /*previousX and previousY are the centre of sequence values calculated in the previous cycle */
f@0 208 int previousX = 0;
f@0 209 int previousY = 0;
f@0 210
f@0 211 for(int i=0; i<sequence.getValuesNum();i++){
f@0 212 Sequence.Value current = sequence.getValueAt(i);
f@0 213 int currentX = (int)(current.getTimePosition()/millisecPerPixel) - (SequencePoint.SIZE/2) ;
f@0 214 /* height is reversed as in swing the top is 0 */
f@0 215 int currentY = height - (int)(scale.linear(current.getValue())) - (SequencePoint.SIZE/2) ;
f@0 216 /* add the point */
f@0 217 sequencePoints.add(new SequencePoint(current,currentX,currentY));
f@0 218 if(previous == null){
f@0 219 /* add the line from the beginning to the first point */
f@0 220 sequenceLines.add(stroke.createStrokedShape(
f@0 221 new Line2D.Float(
f@0 222 0f,
f@0 223 height - scale.linear(sequence.getBegin()), /* height is reversed as in swing the top is 0 */
f@0 224 currentX + (SequencePoint.SIZE/2),
f@0 225 currentY + (SequencePoint.SIZE/2) )));
f@0 226 }else{
f@0 227 /* add the line with previous point */
f@0 228 sequenceLines.add(stroke.createStrokedShape(
f@0 229 new Line2D.Float(
f@0 230 previousX + (SequencePoint.SIZE/2),
f@0 231 previousY + (SequencePoint.SIZE/2),
f@0 232 currentX + (SequencePoint.SIZE/2),
f@0 233 currentY + (SequencePoint.SIZE/2) )));
f@0 234 }
f@0 235 /* prepare for next cycle */
f@0 236 previous = current;
f@0 237 previousX = currentX;
f@0 238 previousY = currentY;
f@0 239 }
f@0 240
f@0 241 /* add the line from the last point to the end */
f@0 242 sequenceLines.add(stroke.createStrokedShape(
f@0 243 new Line2D.Float(
f@0 244 previousX + (SequencePoint.SIZE/2),
f@0 245 previousY + (SequencePoint.SIZE/2),
f@0 246 width,
f@0 247 height - scale.linear(sequence.getEnd()) ))); /* height is reversed as in swing the top is 0 */
f@0 248 }
f@0 249
f@0 250 fireChangeListeners();
f@0 251 }
f@0 252
f@0 253 }
f@0 254