/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