/Users/lyon/j4p/src/sound/soundDemo/TempoDial.java
|
1 package sound.soundDemo;
2
3 /*
4 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
5 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6 */
7
8
9 import javax.sound.midi.Sequencer;
10 import javax.swing.*;
11 import java.awt.*;
12 import java.awt.event.*;
13 import java.awt.geom.*;
14 import java.util.Vector;
15
16
17 /**
18 * Midi tempo dial in beats per minute.
19 *
20 * @version @(#)TempoDial.java 1.9 02/02/06
21 * @author Brian Lichtenwalter
22 */
23 public class TempoDial extends JPanel {
24
25 private int dotSize = 6;
26 private Ellipse2D ellipse;
27 private Vector data;
28 private Data currentData;
29 private Sequencer sequencer;
30
31
32 public TempoDial() {
33 setBackground(new Color(20, 20, 20));
34
35 ellipse = new Ellipse2D.Float(2, 20, 92, 120);
36 Vector dots = new Vector();
37 PathIterator pi = ellipse.getPathIterator(null, 0.9);
38 while (!pi.isDone()) {
39 float[] pt = new float[6];
40 switch (pi.currentSegment(pt)) {
41 case FlatteningPathIterator.SEG_MOVETO:
42 case FlatteningPathIterator.SEG_LINETO:
43 dots.add(new Ellipse2D.Float(pt[0], pt[1], dotSize, dotSize));
44 }
45 pi.next();
46 }
47 Vector tmp = new Vector();
48 for (int i = 0; i < dots.size(); i++) {
49 if (((Ellipse2D) dots.get(i)).getY() >= ellipse.getHeight() / 2) {
50 tmp.add(dots.get(i));
51 }
52 }
53 dots.removeAll(tmp);
54
55 float x = (float) (ellipse.getX() + ellipse.getWidth() / 2);
56 float y = (float) (ellipse.getY() + (ellipse.getHeight() / 2));
57 Vector paths = new Vector(dots.size());
58 for (int i = 0; i < dots.size(); i++) {
59 GeneralPath gp = new GeneralPath(GeneralPath.WIND_NON_ZERO);
60 gp.moveTo(x, y);
61 Ellipse2D e1 = (Ellipse2D) dots.get(i);
62 gp.lineTo((float) e1.getX(), (float) e1.getY());
63 if (i + 1 < dots.size()) {
64 Ellipse2D e2 = (Ellipse2D) dots.get(i + 1);
65 gp.lineTo((float) e2.getX(), (float) e2.getY());
66 }
67 gp.closePath();
68 paths.add(gp);
69 }
70
71 data = new Vector(paths.size());
72 for (int i = 0, tempo = 40; i < paths.size(); i++, tempo += 10) {
73 data.add(new Data(tempo, dots.get(i), paths.get(i)));
74 if (tempo == 120) {
75 currentData = (Data) data.lastElement();
76 }
77 }
78
79 addMouseMotionListener(new MouseMotionAdapter() {
80 public void mouseDragged(MouseEvent e) {
81 processMouse(e);
82 }
83 });
84 addMouseListener(new MouseAdapter() {
85 public void mouseClicked(MouseEvent e) {
86 processMouse(e);
87 }
88 });
89 }
90
91
92 private void processMouse(MouseEvent e) {
93 if (ellipse.contains(e.getPoint())) {
94 for (int i = 0; i < data.size(); i++) {
95 currentData = (Data) data.get(i);
96 if (currentData.path.contains(e.getPoint())) {
97 break;
98 }
99 }
100 repaint();
101 if (sequencer != null) {
102 sequencer.setTempoInBPM((float) getTempo());
103 }
104 }
105 }
106
107
108 public void setSequencer(Sequencer sequencer) {
109 this.sequencer = sequencer;
110 }
111
112
113 public float getTempo() {
114 return ((float) currentData.tempo);
115 }
116
117
118 /**
119 * Tempo value must match one found in data vector.
120 * Acceptable tempo values start at 40 increment by 10 until 160.
121 */
122 public void setTempo(float tempo) {
123 for (int i = 0; i < data.size(); i++) {
124 currentData = (Data) data.get(i);
125 if (currentData.tempo == tempo) {
126 break;
127 }
128 }
129 repaint();
130 }
131
132
133 public void paint(Graphics g) {
134 Dimension d = getSize();
135 Graphics2D g2 = (Graphics2D) g;
136 g2.setBackground(getBackground());
137 g2.clearRect(0, 0, d.width, d.height);
138 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
139 RenderingHints.VALUE_ANTIALIAS_ON);
140
141 double x = ellipse.getWidth() / 2 + ellipse.getX() + dotSize / 2;
142 double y = ellipse.getHeight() / 2;
143 double x2 = currentData.dot.getX() + dotSize / 2;
144 double y2 = currentData.dot.getY() + dotSize / 2;
145 Ellipse2D e = new Ellipse2D.Double(x - 5, y - 5, 10, 10);
146
147 Color jfcBlue = new Color(204, 204, 255);
148 g2.setColor(jfcBlue);
149 g2.setStroke(new BasicStroke(3));
150 g2.draw(new Line2D.Double(e.getX() + 5, e.getY() + 5, x2, y2));
151 g2.fill(e);
152 g2.setFont(new Font("serif", Font.BOLD, 12));
153 g2.drawString(String.valueOf(currentData.tempo) + " bpm", 2, 12);
154
155 g2.fill(currentData.dot);
156 g2.setStroke(new BasicStroke(1.5f));
157 g2.setColor(jfcBlue.darker());
158 for (int i = 0; i < data.size(); i++) {
159 g2.draw(((Data) data.get(i)).dot);
160 }
161 }
162
163
164 public Dimension getPreferredSize() {
165 return new Dimension(105, 70);
166 }
167
168 public Dimension getMaximumSize() {
169 return getPreferredSize();
170 }
171
172
173 /**
174 * Convenience storage class for our tempo dial data.
175 */
176 class Data extends Object {
177 int tempo;
178 Ellipse2D dot;
179 GeneralPath path;
180
181 public Data(int tempo, Object dot, Object path) {
182 this.tempo = tempo;
183 this.dot = (Ellipse2D) dot;
184 this.path = (GeneralPath) path;
185 }
186 }
187
188
189 public static void main(String argv[]) {
190 JFrame f = new JFrame("Tempo Dial");
191 f.addWindowListener(new WindowAdapter() {
192 public void windowClosing(WindowEvent e) {
193 System.exit(0);
194 }
195 });
196 f.getContentPane().add("Center", new TempoDial());
197 f.pack();
198 f.setSize(new Dimension(200, 140));
199 f.setVisible(true);
200 }
201 }
202