/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