/Users/lyon/j4p/src/sound/soundDemo/Groove.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 sound.musica.Utils; 
10    
11   import javax.sound.midi.*; 
12   import javax.swing.*; 
13   import javax.swing.border.BevelBorder; 
14   import javax.swing.border.CompoundBorder; 
15   import javax.swing.border.EmptyBorder; 
16   import javax.swing.border.SoftBevelBorder; 
17   import javax.swing.event.ListSelectionEvent; 
18   import javax.swing.event.ListSelectionListener; 
19   import javax.swing.event.TableModelEvent; 
20   import javax.swing.table.*; 
21   import java.awt.*; 
22   import java.awt.event.ActionEvent; 
23   import java.awt.event.ActionListener; 
24   import java.awt.event.WindowAdapter; 
25   import java.awt.event.WindowEvent; 
26   import java.util.Vector; 
27    
28   /** 
29    * Rhythm Groove Box.  Program any beat you like, click on a cell. 
30    * Channel 10 (the rhythm channel) supports the 47 instrument sounds. 
31    * These sounds are the result of a program change to instrument 1. 
32    * 
33    * Beat Pattern 1 
34    * 
35    *     |  1 sec         | 2 sec 
36    *     1 e + a  2 e + a   3 e + a  4 e + a 
37    * hh  x   x    x   x     x   x    x   x 
38    * sn           x                  x 
39    * kk  x     x      x     x 
40    *     0 1 2 3  4 5 6 7   8 9 1011 12131415 
41    * 
42    * Hi-hat 
43    * on-off : 0-1, 2-3, 4-5, 6-7, 8-9, 10-11, 12-13, 14-15 
44    * 
45    * snare : 
46    * on-off : 4-5, 12-13 
47    * 
48    * bass : 
49    * on-off : 0-1, 3-4, 6-7, 8-9 
50    * 
51    * @version @(#)Groove.java 1.17 02/02/06 
52    * @author Brian Lichtenwalter 
53    */ 
54   public class Groove extends JPanel implements ActionListener, ControlContext, MetaEventListener { 
55    
56       final int PROGRAM = 192; 
57       final int NOTEON = 144; 
58       final int NOTEOFF = 128; 
59       int velocity = 100; 
60       TempoDial tempoDial = new TempoDial(); 
61       Sequencer sequencer = null; 
62       Track track; 
63       TableModel dataModel; 
64       JTable table; 
65       int row, col; 
66       JButton loopB, startB; 
67       JComboBox combo; 
68       Vector data = new Vector(Utils.instruments.length); 
69    
70    
71       public Groove() { 
72           setLayout(new BorderLayout(5, 0)); 
73           EmptyBorder eb = new EmptyBorder(5, 5, 5, 5); 
74           setBorder(eb); 
75    
76           for (int i = 0, id = 35; i < Utils.instruments.length; i++, id++) { 
77               data.add(new Data(Utils.instruments[i], id)); 
78           } 
79    
80           final String[] names = {"Instrument", 
81                                   "1", "e", "+", "a", 
82                                   "2", "e", "+", "a", 
83                                   "3", "e", "+", "a", 
84                                   "4", "e", "+", "a"}; 
85    
86           dataModel = new AbstractTableModel() { 
87               public int getColumnCount() { 
88                   return names.length; 
89               } 
90    
91               public int getRowCount() { 
92                   return data.size(); 
93               } 
94    
95               public Object getValueAt(int row, int col) { 
96                   if (col == 0) { 
97                       return ((Data) data.get(row)).name; 
98                   } else { 
99                       return ((Data) data.get(row)).staff[col - 1]; 
100                  } 
101              } 
102   
103              public String getColumnName(int col) { 
104                  return names[col]; 
105              } 
106   
107              public Class getColumnClass(int c) { 
108                  return getValueAt(0, c).getClass(); 
109              } 
110   
111              public boolean isCellEditable(int row, int col) { 
112                  return col == 0 ? false : true; 
113              } 
114   
115              public void setValueAt(Object aValue, int row, int col) { 
116                  if (col == 0) { 
117                      ((Data) data.get(row)).name = (String) aValue; 
118                  } else { 
119                      ((Data) data.get(row)).staff[col - 1] = (Color) aValue; 
120                  } 
121              } 
122          }; 
123   
124          DefaultTableCellRenderer renderer = new DefaultTableCellRenderer() { 
125              public void setValue(Object value) { 
126                  setBackground((Color) value); 
127              } 
128          }; 
129   
130          table = new JTable(dataModel); 
131          table.getColumn(names[0]).setMinWidth(120); 
132          TableColumnModel tcm = table.getColumnModel(); 
133          for (int i = 1; i < names.length; i++) { 
134              TableColumn col = tcm.getColumn(i); 
135              col.setCellRenderer(renderer); 
136          } 
137   
138          // Listener for row changes 
139          ListSelectionModel lsm = table.getSelectionModel(); 
140          lsm.addListSelectionListener(new ListSelectionListener() { 
141              public void valueChanged(ListSelectionEvent e) { 
142                  ListSelectionModel sm = (ListSelectionModel) e.getSource(); 
143                  if (!sm.isSelectionEmpty()) { 
144                      row = sm.getMinSelectionIndex(); 
145                  } 
146              } 
147          }); 
148   
149          // Listener for column changes 
150          lsm = table.getColumnModel().getSelectionModel(); 
151          lsm.addListSelectionListener(new ListSelectionListener() { 
152              public void valueChanged(ListSelectionEvent e) { 
153                  ListSelectionModel sm = (ListSelectionModel) e.getSource(); 
154                  if (!sm.isSelectionEmpty()) { 
155                      col = sm.getMinSelectionIndex(); 
156                  } 
157                  if (col != 0) { 
158                      Color c = ((Data) data.get(row)).staff[col - 1]; 
159                      if (c.equals(Color.white)) { 
160                          ((Data) data.get(row)).staff[col - 1] = Color.black; 
161                      } else { 
162                          ((Data) data.get(row)).staff[col - 1] = Color.white; 
163                      } 
164                      table.tableChanged(new TableModelEvent(dataModel)); 
165                  } 
166              } 
167          }); 
168   
169          JPanel p1 = new JPanel(); 
170          p1.setLayout(new BoxLayout(p1, BoxLayout.Y_AXIS)); 
171          SoftBevelBorder sbb = new SoftBevelBorder(BevelBorder.RAISED); 
172          p1.setBorder(new CompoundBorder(sbb, eb)); 
173          p1.add(tempoDial); 
174          p1.add(Box.createVerticalStrut(10)); 
175   
176          JPanel p2 = new JPanel(new GridLayout(0, 1, 2, 10)); 
177          p2.add(startB = makeButton("Start", getBackground())); 
178          p2.add(loopB = makeButton("Loop", getBackground())); 
179          p2.add(makeButton("Clear Table", getBackground())); 
180   
181          combo = new JComboBox(); 
182          combo.addActionListener(this); 
183          combo.addItem("Rock Beat 1"); 
184          combo.addItem("Rock Beat 2"); 
185          combo.addItem("Rock Beat 3"); 
186          p2.add(combo); 
187   
188          p1.add(p2); 
189          p1.add(Box.createVerticalStrut(120)); 
190          add("West", p1); 
191   
192          add("Center", new JScrollPane(table)); 
193      } 
194   
195   
196      public void open() { 
197          try { 
198              sequencer = Utils.getAndOpenSequencer(); 
199          } catch (MidiUnavailableException e) { 
200              e.printStackTrace(); 
201          } 
202          tempoDial.setSequencer(sequencer); 
203          sequencer.addMetaEventListener(this); 
204      } 
205   
206   
207      public void close() { 
208          if (startB.getText().startsWith("Stop")) { 
209              startB.doClick(0); 
210          } 
211          if (sequencer != null) { 
212              sequencer.close(); 
213          } 
214          sequencer = null; 
215      } 
216   
217   
218      private JButton makeButton(String bName, Color c) { 
219          JButton b = new JButton(bName); 
220          b.setBackground(c); 
221          b.addActionListener(this); 
222          return b; 
223      } 
224   
225   
226      private void buildTrackThenStartSequencer() { 
227          Sequence sequence = null; 
228          try { 
229              sequence = new Sequence(Sequence.PPQ, 4); 
230          } catch (Exception ex) { 
231              ex.printStackTrace(); 
232          } 
233          track = sequence.createTrack(); 
234          createEvent(PROGRAM, 9, 1, 0); 
235          for (int i = 0; i < data.size(); i++) { 
236              Data d = (Data) data.get(i); 
237              for (int j = 0; j < d.staff.length; j++) { 
238                  if (d.staff[j].equals(Color.black)) { 
239                      createEvent(NOTEON, 9, d.id, j); 
240                      createEvent(NOTEOFF, 9, d.id, j + 1); 
241                  } 
242              } 
243          } 
244          // so we always have a track from 0 to 15. 
245          createEvent(PROGRAM, 9, 1, 15); 
246   
247          // set and start the sequencer. 
248          try { 
249              sequencer.setSequence(sequence); 
250          } catch (Exception ex) { 
251              ex.printStackTrace(); 
252          } 
253          sequencer.start(); 
254          sequencer.setTempoInBPM(tempoDial.getTempo()); 
255      } 
256   
257   
258      private void presetTracks(int num) { 
259   
260          final int ACOUSTIC_BASS = 35; 
261          final int ACOUSTIC_SNARE = 38; 
262          final int HAND_CLAP = 39; 
263          final int PEDAL_HIHAT = 44; 
264          final int LO_TOM = 45; 
265          final int CLOSED_HIHAT = 42; 
266          final int CRASH_CYMBAL1 = 49; 
267          final int HI_TOM = 50; 
268          final int RIDE_BELL = 53; 
269   
270          clearTable(); 
271   
272          switch (num) { 
273              case 0: 
274                  for (int i = 0; i < 16; i += 2) { 
275                      setCell(CLOSED_HIHAT, i); 
276                  } 
277                  setCell(ACOUSTIC_SNARE, 4); 
278                  setCell(ACOUSTIC_SNARE, 12); 
279                  int bass1[] = {0, 3, 6, 8}; 
280                  for (int i = 0; i < bass1.length; i++) { 
281                      setCell(ACOUSTIC_BASS, bass1[i]); 
282                  } 
283                  break; 
284              case 1: 
285                  for (int i = 0; i < 16; i += 4) { 
286                      setCell(CRASH_CYMBAL1, i); 
287                  } 
288                  for (int i = 0; i < 16; i += 2) { 
289                      setCell(PEDAL_HIHAT, i); 
290                  } 
291                  setCell(ACOUSTIC_SNARE, 4); 
292                  setCell(ACOUSTIC_SNARE, 12); 
293                  int bass2[] = {0, 2, 3, 7, 9, 10, 15}; 
294                  for (int i = 0; i < bass2.length; i++) { 
295                      setCell(ACOUSTIC_BASS, bass2[i]); 
296                  } 
297                  break; 
298              case 2: 
299                  for (int i = 0; i < 16; i += 4) { 
300                      setCell(RIDE_BELL, i); 
301                  } 
302                  for (int i = 2; i < 16; i += 4) { 
303                      setCell(PEDAL_HIHAT, i); 
304                  } 
305                  setCell(HAND_CLAP, 4); 
306                  setCell(HAND_CLAP, 12); 
307                  setCell(HI_TOM, 13); 
308                  setCell(LO_TOM, 14); 
309                  int bass3[] = {0, 3, 6, 9, 15}; 
310                  for (int i = 0; i < bass3.length; i++) { 
311                      setCell(ACOUSTIC_BASS + 1, bass3[i]); 
312                  } 
313                  break; 
314              default : 
315          } 
316          table.tableChanged(new TableModelEvent(dataModel)); 
317      } 
318   
319   
320      private void setCell(int id, int tick) { 
321          for (int i = 0; i < data.size(); i++) { 
322              Data d = (Data) data.get(i); 
323              if (d.id == id) { 
324                  d.staff[tick] = Color.black; 
325                  break; 
326              } 
327          } 
328      } 
329   
330   
331      private void clearTable() { 
332          for (int i = 0; i < data.size(); i++) { 
333              Data d = (Data) data.get(i); 
334              for (int j = 0; j < d.staff.length; j++) { 
335                  d.staff[j] = Color.white; 
336              } 
337          } 
338      } 
339   
340   
341      private void createEvent(int type, int chan, int num, long tick) { 
342          ShortMessage message = new ShortMessage(); 
343          try { 
344              message.setMessage(type, chan, num, velocity); 
345              MidiEvent event = new MidiEvent(message, tick); 
346              track.add(event); 
347          } catch (InvalidMidiDataException e) { 
348              e.printStackTrace(); 
349          } 
350   
351      } 
352   
353   
354      public void meta(MetaMessage message) { 
355          if (message.getType() == 47) {  // 47 is end of track 
356              if (loopB.getBackground().equals(Color.gray)) { 
357                  if (sequencer != null && sequencer.isOpen()) { 
358                      sequencer.start(); 
359                      sequencer.setTempoInBPM(tempoDial.getTempo()); 
360                  } 
361              } else { 
362                  startB.setText("Start"); 
363              } 
364          } 
365      } 
366   
367   
368      public void actionPerformed(ActionEvent e) { 
369          Object object = e.getSource(); 
370          if (object instanceof JComboBox) { 
371              presetTracks(((JComboBox) object).getSelectedIndex()); 
372              if (startB.getText().startsWith("Stop")) { 
373                  sequencer.stop(); 
374                  buildTrackThenStartSequencer(); 
375              } 
376          } else if (object instanceof JButton) { 
377              JButton b = (JButton) object; 
378              if (b.equals(startB)) { 
379                  if (b.getText().startsWith("Start")) { 
380                      buildTrackThenStartSequencer(); 
381                      b.setText("Stop"); 
382                  } else { 
383                      sequencer.stop(); 
384                      b.setText("Start"); 
385                  } 
386              } else if (b.equals(loopB)) { 
387                  b.setSelected(!b.isSelected()); 
388                  if (loopB.getBackground().equals(Color.gray)) { 
389                      loopB.setBackground(getBackground()); 
390                  } else { 
391                      loopB.setBackground(Color.gray); 
392                  } 
393              } else if (b.getText().startsWith("Clear")) { 
394                  clearTable(); 
395                  table.tableChanged(new TableModelEvent(dataModel)); 
396              } 
397          } 
398      } 
399   
400   
401      /** 
402       * Storage class for instrument and musical staff represented by color. 
403       */ 
404      class Data extends Object { 
405          String name; 
406          int id; 
407          Color staff[] = new Color[16]; 
408   
409          public Data(String name, int id) { 
410              this.name = name; 
411              this.id = id; 
412              for (int i = 0; i < staff.length; i++) { 
413                  staff[i] = Color.white; 
414              } 
415          } 
416      } 
417   
418   
419      public static void main(String args[]) { 
420          final Groove groove = new Groove(); 
421          JFrame f = new JFrame("Rhythm Groove Box"); 
422          f.addWindowListener(new WindowAdapter() { 
423              public void windowClosing(WindowEvent e) { 
424                  System.exit(0); 
425              } 
426          }); 
427          f.getContentPane().add("Center", groove); 
428          f.pack(); 
429          Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 
430          int w = 640; 
431          int h = 440; 
432          f.setLocation(screenSize.width / 2 - w / 2, screenSize.height / 2 - h / 2); 
433          f.setSize(w, h); 
434          f.show(); 
435          groove.open(); 
436      } 
437  } 
438