/Users/lyon/j4p/src/sound/soundDemo/Juke.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.*;
10 import javax.sound.sampled.*;
11 import javax.swing.*;
12 import javax.swing.border.*;
13 import javax.swing.event.ChangeEvent;
14 import javax.swing.event.ChangeListener;
15 import javax.swing.event.TableModelEvent;
16 import javax.swing.table.AbstractTableModel;
17 import javax.swing.table.TableColumn;
18 import javax.swing.table.TableModel;
19 import java.awt.*;
20 import java.awt.event.ActionEvent;
21 import java.awt.event.ActionListener;
22 import java.awt.event.WindowAdapter;
23 import java.awt.event.WindowEvent;
24 import java.awt.font.FontRenderContext;
25 import java.awt.font.LineBreakMeasurer;
26 import java.awt.font.TextAttribute;
27 import java.awt.font.TextLayout;
28 import java.awt.geom.Arc2D;
29 import java.awt.geom.Ellipse2D;
30 import java.awt.geom.Rectangle2D;
31 import java.io.BufferedInputStream;
32 import java.io.File;
33 import java.io.FileInputStream;
34 import java.net.URL;
35 import java.text.AttributedCharacterIterator;
36 import java.text.AttributedString;
37 import java.util.Vector;
38
39
40 /**
41 * A JukeBox for sampled and midi sound files. Features duration progress,
42 * seek slider, pan and volume controls.
43 *
44 * @version @(#)Juke.java 1.21 02/02/06
45 * @author Brian Lichtenwalter
46 */
47 public class Juke extends JPanel implements Runnable, LineListener, MetaEventListener, ControlContext {
48
49 final int bufSize = 16384;
50 PlaybackMonitor playbackMonitor = new PlaybackMonitor();
51
52 Vector sounds = new Vector();
53 Thread thread;
54 Sequencer sequencer;
55 boolean midiEOM, audioEOM;
56 Synthesizer synthesizer;
57 MidiChannel channels[];
58 Object currentSound;
59 String currentName;
60 double duration;
61 int num;
62 boolean bump;
63 boolean paused = false;
64 JButton startB, pauseB, loopB, prevB, nextB;
65 JTable table;
66 JSlider panSlider, gainSlider;
67 JSlider seekSlider;
68 JukeTable jukeTable;
69 Loading loading;
70 Credits credits;
71 String errStr;
72 JukeControls controls;
73
74
75 public Juke(String dirName) {
76 setLayout(new BorderLayout());
77 setBorder(new EmptyBorder(5, 5, 5, 5));
78
79 if (dirName != null) {
80 loadJuke(dirName);
81 }
82
83 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
84 jukeTable = new JukeTable(), controls = new JukeControls());
85 splitPane.setContinuousLayout(true);
86 add(splitPane);
87 }
88
89
90 public void open() {
91
92 try {
93
94 sequencer = MidiSystem.getSequencer();
95
96 if (sequencer instanceof Synthesizer) {
97 synthesizer = (Synthesizer) sequencer;
98 channels = synthesizer.getChannels();
99 }
100
101 } catch (Exception ex) {
102 ex.printStackTrace();
103 return;
104 }
105 sequencer.addMetaEventListener(this);
106 (credits = new Credits()).start();
107 }
108
109
110 public void close() {
111 if (credits != null && credits.isAlive()) {
112 credits.interrupt();
113 }
114 if (thread != null && startB != null) {
115 startB.doClick(0);
116 }
117 if (jukeTable != null && jukeTable.frame != null) {
118 jukeTable.frame.dispose();
119 jukeTable.frame = null;
120 }
121 if (sequencer != null) {
122 sequencer.close();
123 }
124 }
125
126
127 public void loadJuke(String name) {
128 try {
129 File file = new File(name);
130 if (file != null && file.isDirectory()) {
131 String files[] = file.list();
132 for (int i = 0; i < files.length; i++) {
133 File leafFile = new File(file.getAbsolutePath(), files[i]);
134 if (leafFile.isDirectory()) {
135 loadJuke(leafFile.getAbsolutePath());
136 } else {
137 addSound(leafFile);
138 }
139 }
140 } else if (file != null && file.exists()) {
141 addSound(file);
142 }
143 } catch (SecurityException ex) {
144 reportStatus(ex.toString());
145 JavaSound.showInfoDialog();
146 } catch (Exception ex) {
147 reportStatus(ex.toString());
148 }
149 }
150
151
152 private void addSound(File file) {
153 String s = file.getName().toLowerCase();
154 if (s.endsWith(".au") || s.endsWith(".rmf") ||
155 s.endsWith(".mid") || s.endsWith(".wav") ||
156 s.endsWith(".aif") || s.endsWith(".aiff")) {
157 sounds.add(file);
158 }
159 }
160
161
162 public boolean loadSound(Object object) {
163
164 duration = 0.0;
165 (loading = new Loading()).start();
166
167 if (object instanceof URL) {
168 currentName = ((URL) object).getFile();
169 playbackMonitor.repaint();
170 try {
171 currentSound = AudioSystem.getAudioInputStream((URL) object);
172 } catch (Exception e) {
173 try {
174 currentSound = MidiSystem.getSequence((URL) object);
175 } catch (InvalidMidiDataException imde) {
176 System.out.println("Unsupported audio file.");
177 return false;
178 } catch (Exception ex) {
179 ex.printStackTrace();
180 currentSound = null;
181 return false;
182 }
183 }
184 } else if (object instanceof File) {
185 currentName = ((File) object).getName();
186 playbackMonitor.repaint();
187 try {
188 currentSound = AudioSystem.getAudioInputStream((File) object);
189 } catch (Exception e1) {
190 // load midi & rmf as inputstreams for now
191 //try {
192 //currentSound = MidiSystem.getSequence((File) object);
193 //} catch (Exception e2) {
194 try {
195 FileInputStream is = new FileInputStream((File) object);
196 currentSound = new BufferedInputStream(is, 1024);
197 } catch (Exception e3) {
198 e3.printStackTrace();
199 currentSound = null;
200 return false;
201 }
202 //}
203 }
204 }
205
206
207 loading.interrupt();
208
209 // user pressed stop or changed tabs while loading
210 if (sequencer == null) {
211 currentSound = null;
212 return false;
213 }
214
215 if (currentSound instanceof AudioInputStream) {
216 try {
217 AudioInputStream stream = (AudioInputStream) currentSound;
218 AudioFormat format = stream.getFormat();
219
220 /**
221 * we can't yet open the device for ALAW/ULAW playback,
222 * convert ALAW/ULAW to PCM
223 */
224 if ((format.getEncoding() == AudioFormat.Encoding.ULAW) ||
225 (format.getEncoding() == AudioFormat.Encoding.ALAW)) {
226 AudioFormat tmp = new AudioFormat(
227 AudioFormat.Encoding.PCM_SIGNED,
228 format.getSampleRate(),
229 format.getSampleSizeInBits() * 2,
230 format.getChannels(),
231 format.getFrameSize() * 2,
232 format.getFrameRate(),
233 true);
234 stream = AudioSystem.getAudioInputStream(tmp, stream);
235 format = tmp;
236 }
237 DataLine.Info info = new DataLine.Info(
238 Clip.class,
239 stream.getFormat(),
240 ((int) stream.getFrameLength() *
241 format.getFrameSize()));
242
243 Clip clip = (Clip) AudioSystem.getLine(info);
244 clip.addLineListener(this);
245 clip.open(stream);
246 currentSound = clip;
247 seekSlider.setMaximum((int) stream.getFrameLength());
248 } catch (Exception ex) {
249 ex.printStackTrace();
250 currentSound = null;
251 return false;
252 }
253 } else if (currentSound instanceof Sequence || currentSound instanceof BufferedInputStream) {
254 try {
255 sequencer.open();
256 if (currentSound instanceof Sequence) {
257 sequencer.setSequence((Sequence) currentSound);
258 } else {
259 sequencer.setSequence((BufferedInputStream) currentSound);
260 }
261 seekSlider.setMaximum((int) (sequencer.getMicrosecondLength() / 1000));
262
263 } catch (InvalidMidiDataException imde) {
264 System.out.println("Unsupported audio file.");
265 currentSound = null;
266 return false;
267 } catch (Exception ex) {
268 ex.printStackTrace();
269 currentSound = null;
270 return false;
271 }
272 }
273
274 seekSlider.setValue(0);
275
276 // enable seek, pan, and gain sliders for sequences as well as clips
277 seekSlider.setEnabled(true);
278 panSlider.setEnabled(true);
279 gainSlider.setEnabled(true);
280
281 duration = getDuration();
282
283 return true;
284 }
285
286
287 public void playSound() {
288 playbackMonitor.start();
289 setGain();
290 setPan();
291 midiEOM = audioEOM = bump = false;
292 if (currentSound instanceof Sequence || currentSound instanceof BufferedInputStream && thread != null) {
293 sequencer.start();
294 while (!midiEOM && thread != null && !bump) {
295 try {
296 Thread.sleep(99);
297 } catch (Exception e) {
298 break;
299 }
300 }
301 sequencer.stop();
302 sequencer.close();
303 } else if (currentSound instanceof Clip && thread != null) {
304 Clip clip = (Clip) currentSound;
305 clip.start();
306 try {
307 Thread.sleep(99);
308 } catch (Exception e) {
309 }
310 while ((paused || clip.isActive()) && thread != null && !bump) {
311 try {
312 Thread.sleep(99);
313 } catch (Exception e) {
314 break;
315 }
316 }
317 clip.stop();
318 clip.close();
319 }
320 currentSound = null;
321 playbackMonitor.stop();
322 }
323
324
325 public double getDuration() {
326 double duration = 0.0;
327 if (currentSound instanceof Sequence) {
328 duration = ((Sequence) currentSound).getMicrosecondLength() / 1000000.0;
329 } else if (currentSound instanceof BufferedInputStream) {
330 duration = sequencer.getMicrosecondLength() / 1000000.0;
331 } else if (currentSound instanceof Clip) {
332 Clip clip = (Clip) currentSound;
333 duration = clip.getBufferSize() /
334 (clip.getFormat().getFrameSize() * clip.getFormat().getFrameRate());
335 }
336 return duration;
337 }
338
339
340 public double getSeconds() {
341 double seconds = 0.0;
342 if (currentSound instanceof Clip) {
343 Clip clip = (Clip) currentSound;
344 seconds = clip.getFramePosition() / clip.getFormat().getFrameRate();
345 } else if ((currentSound instanceof Sequence) || (currentSound instanceof BufferedInputStream)) {
346 try {
347 seconds = sequencer.getMicrosecondPosition() / 1000000.0;
348 } catch (IllegalStateException e) {
349 System.out.println("TEMP: IllegalStateException " +
350 "on sequencer.getMicrosecondPosition(): " + e);
351 }
352 }
353 return seconds;
354 }
355
356
357 public void update(LineEvent event) {
358 if (event.getType() == LineEvent.Type.STOP && !paused) {
359 audioEOM = true;
360 }
361 }
362
363
364 public void meta(MetaMessage message) {
365 if (message.getType() == 47) { // 47 is end of track
366 midiEOM = true;
367 }
368 }
369
370
371 private void reportStatus(String msg) {
372 if ((errStr = msg) != null) {
373 System.out.println(errStr);
374 playbackMonitor.repaint();
375 }
376 if (credits != null && credits.isAlive()) {
377 credits.interrupt();
378 }
379 }
380
381
382 public Thread getThread() {
383 return thread;
384 }
385
386
387 public void start() {
388 thread = new Thread(this);
389 thread.setName("Juke");
390 thread.start();
391 }
392
393
394 public void stop() {
395 if (thread != null) {
396 thread.interrupt();
397 }
398 thread = null;
399 }
400
401
402 public void run() {
403 do {
404 table.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
405 for (; num < sounds.size() && thread != null; num++) {
406 table.scrollRectToVisible(new Rectangle(0, (num + 2) * (table.getRowHeight() + table.getRowMargin()), 1, 1));
407 table.setRowSelectionInterval(num, num);
408 if (loadSound(sounds.get(num)) == true) {
409 playSound();
410 }
411 // take a little break between sounds
412 try {
413 Thread.sleep(222);
414 } catch (Exception e) {
415 break;
416 }
417 }
418 num = 0;
419 } while (loopB.isSelected() && thread != null);
420
421 if (thread != null) {
422 startB.doClick();
423 }
424 thread = null;
425 currentName = null;
426 currentSound = null;
427 playbackMonitor.repaint();
428 }
429
430
431 public void setPan() {
432
433 int value = panSlider.getValue();
434
435 if (currentSound instanceof Clip) {
436 try {
437 Clip clip = (Clip) currentSound;
438 FloatControl panControl =
439 (FloatControl) clip.getControl(FloatControl.Type.PAN);
440 panControl.setValue(value / 100.0f);
441 } catch (Exception ex) {
442 ex.printStackTrace();
443 }
444 } else if (currentSound instanceof Sequence || currentSound instanceof BufferedInputStream) {
445 for (int i = 0; i < channels.length; i++) {
446 channels[i].controlChange(10, (int) (((double) value + 100.0) / 200.0 * 127.0));
447 }
448 }
449 }
450
451
452 public void setGain() {
453 double value = gainSlider.getValue() / 100.0;
454
455 if (currentSound instanceof Clip) {
456 try {
457 Clip clip = (Clip) currentSound;
458 FloatControl gainControl =
459 (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
460 float dB = (float)
461 (Math.log(value == 0.0 ? 0.0001 : value) / Math.log(10.0) * 20.0);
462 gainControl.setValue(dB);
463 } catch (Exception ex) {
464 ex.printStackTrace();
465 }
466 } else if (currentSound instanceof Sequence || currentSound instanceof BufferedInputStream) {
467 for (int i = 0; i < channels.length; i++) {
468 channels[i].controlChange(7, (int) (value * 127.0));
469
470 }
471 }
472 }
473
474
475 /**
476 * GUI controls for start, stop, previous, next, pan and gain.
477 */
478 class JukeControls extends JPanel implements ActionListener, ChangeListener {
479
480 public JukeControls() {
481 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
482
483 JPanel p1 = new JPanel();
484 p1.setLayout(new BoxLayout(p1, BoxLayout.Y_AXIS));
485 p1.setBorder(new EmptyBorder(10, 0, 5, 0));
486 JPanel p2 = new JPanel();
487 startB = addButton("Start", p2, sounds.size() != 0);
488 pauseB = addButton("Pause", p2, false);
489 p1.add(p2);
490 JPanel p3 = new JPanel();
491 prevB = addButton("<<", p3, false);
492 nextB = addButton(">>", p3, false);
493 p1.add(p3);
494 add(p1);
495
496 JPanel p4 = new JPanel(new BorderLayout());
497 EmptyBorder eb = new EmptyBorder(5, 20, 10, 20);
498 BevelBorder bb = new BevelBorder(BevelBorder.LOWERED);
499 p4.setBorder(new CompoundBorder(eb, bb));
500 p4.add(playbackMonitor);
501 seekSlider = new JSlider(JSlider.HORIZONTAL, 0, 100, 0);
502 seekSlider.setEnabled(false);
503 seekSlider.addChangeListener(this);
504 p4.add("South", seekSlider);
505 add(p4);
506
507 JPanel p5 = new JPanel();
508 p5.setLayout(new BoxLayout(p5, BoxLayout.X_AXIS));
509 p5.setBorder(new EmptyBorder(5, 5, 10, 5));
510 panSlider = new JSlider(-100, 100, 0);
511 panSlider.addChangeListener(this);
512 TitledBorder tb = new TitledBorder(new EtchedBorder());
513 tb.setTitle("Pan = 0.0");
514 panSlider.setBorder(tb);
515 p5.add(panSlider);
516 gainSlider = new JSlider(0, 100, 80);
517 gainSlider.addChangeListener(this);
518 tb = new TitledBorder(new EtchedBorder());
519 tb.setTitle("Gain = 80");
520 gainSlider.setBorder(tb);
521 p5.add(gainSlider);
522 add(p5);
523 }
524
525 private JButton addButton(String name, JPanel panel, boolean state) {
526 JButton b = new JButton(name);
527 b.addActionListener(this);
528 b.setEnabled(state);
529 panel.add(b);
530 return b;
531 }
532
533 public void stateChanged(ChangeEvent e) {
534 JSlider slider = (JSlider) e.getSource();
535 int value = slider.getValue();
536 if (slider.equals(seekSlider)) {
537 if (currentSound instanceof Clip) {
538 ((Clip) currentSound).setFramePosition(value);
539 } else if (currentSound instanceof Sequence) {
540 long dur = ((Sequence) currentSound).getMicrosecondLength();
541 sequencer.setMicrosecondPosition(value * 1000);
542 } else if (currentSound instanceof BufferedInputStream) {
543 long dur = sequencer.getMicrosecondLength();
544 sequencer.setMicrosecondPosition(value * 1000);
545 }
546 playbackMonitor.repaint();
547 return;
548 }
549 TitledBorder tb = (TitledBorder) slider.getBorder();
550 String s = tb.getTitle();
551 if (s.startsWith("Pan")) {
552 s = s.substring(0, s.indexOf('=') + 1) +
553 String.valueOf(value / 100.0);
554 if (currentSound != null) {
555 setPan();
556 }
557 } else if (s.startsWith("Gain")) {
558 s = s.substring(0, s.indexOf('=') + 1) +
559 String.valueOf(value);
560 if (currentSound != null) {
561 setGain();
562 }
563 }
564 tb.setTitle(s);
565 slider.repaint();
566 }
567
568
569 public void setComponentsEnabled(boolean state) {
570 seekSlider.setEnabled(state);
571 pauseB.setEnabled(state);
572 prevB.setEnabled(state);
573 nextB.setEnabled(state);
574 }
575
576
577 public void actionPerformed(ActionEvent e) {
578 JButton button = (JButton) e.getSource();
579 if (button.getText().equals("Start")) {
580 if (credits != null) {
581 credits.interrupt();
582 }
583 paused = false;
584 num = table.getSelectedRow();
585 num = num == -1 ? 0 : num;
586 start();
587 button.setText("Stop");
588 setComponentsEnabled(true);
589 } else if (button.getText().equals("Stop")) {
590 credits = new Credits();
591 credits.start();
592 paused = false;
593 stop();
594 button.setText("Start");
595 pauseB.setText("Pause");
596 setComponentsEnabled(false);
597 } else if (button.getText().equals("Pause")) {
598 paused = true;
599 if (currentSound instanceof Clip) {
600 ((Clip) currentSound).stop();
601 } else if (currentSound instanceof Sequence || currentSound instanceof BufferedInputStream) {
602 sequencer.stop();
603 }
604 playbackMonitor.stop();
605 pauseB.setText("Resume");
606 } else if (button.getText().equals("Resume")) {
607 paused = false;
608 if (currentSound instanceof Clip) {
609 ((Clip) currentSound).start();
610 } else if (currentSound instanceof Sequence || currentSound instanceof BufferedInputStream) {
611 sequencer.start();
612 }
613 playbackMonitor.start();
614 pauseB.setText("Pause");
615 } else if (button.getText().equals("<<")) {
616 paused = false;
617 pauseB.setText("Pause");
618 num = num - 1 < 0 ? sounds.size() - 1 : num - 2;
619 bump = true;
620 } else if (button.getText().equals(">>")) {
621 paused = false;
622 pauseB.setText("Pause");
623 num = num + 1 == sounds.size() ? -1 : num;
624 bump = true;
625 }
626 }
627 } // End JukeControls
628
629
630 /**
631 * Displays current sound and time elapsed.
632 */
633 public class PlaybackMonitor extends JPanel implements Runnable {
634
635 String welcomeStr = "Welcome to Java Sound";
636 Thread pbThread;
637 Color black = new Color(20, 20, 20);
638 Color jfcBlue = new Color(204, 204, 255);
639 Color jfcDarkBlue = jfcBlue.darker();
640 Font font24 = new Font("serif", Font.BOLD, 24);
641 Font font28 = new Font("serif", Font.BOLD, 28);
642 Font font42 = new Font("serif", Font.BOLD, 42);
643 FontMetrics fm28, fm42;
644
645 public PlaybackMonitor() {
646 fm28 = getFontMetrics(font28);
647 fm42 = getFontMetrics(font42);
648 }
649
650 public void paint(Graphics g) {
651 Graphics2D g2 = (Graphics2D) g;
652 Dimension d = getSize();
653 g2.setBackground(black);
654 g2.clearRect(0, 0, d.width, d.height);
655 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
656 g2.setColor(jfcBlue);
657
658 if (errStr != null) {
659 g2.setFont(new Font("serif", Font.BOLD, 18));
660 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
661 g2.drawString("ERROR", 5, 20);
662 AttributedString as = new AttributedString(errStr);
663 Font font12 = new Font("serif", Font.PLAIN, 12);
664 as.addAttribute(TextAttribute.FONT, font12, 0, errStr.length());
665 AttributedCharacterIterator aci = as.getIterator();
666 FontRenderContext frc = g2.getFontRenderContext();
667 LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
668 float x = 5, y = 25;
669 lbm.setPosition(0);
670 while (lbm.getPosition() < errStr.length()) {
671 TextLayout tl = lbm.nextLayout(d.width - x - 5);
672 if (!tl.isLeftToRight()) {
673 x = d.width - tl.getAdvance();
674 }
675 tl.draw(g2, x, y += tl.getAscent());
676 y += tl.getDescent() + tl.getLeading();
677 }
678 } else if (currentName == null) {
679 FontRenderContext frc = g2.getFontRenderContext();
680 TextLayout tl = new TextLayout(welcomeStr, font28, frc);
681 float x = (float) (d.width / 2 - tl.getBounds().getWidth() / 2);
682 tl.draw(g2, x, d.height / 2);
683 if (credits != null) {
684 credits.render(d, g2);
685 }
686 } else {
687 g2.setFont(font24);
688 g2.drawString(currentName, 5, fm28.getHeight() - 5);
689 if (duration <= 0.0) {
690 loading.render(d, g2);
691 } else {
692 double seconds = getSeconds();
693 if (midiEOM || audioEOM) {
694 seconds = duration;
695 }
696 if (seconds > 0.0) {
697 g2.setFont(font42);
698 String s = String.valueOf(seconds);
699 s = s.substring(0, s.indexOf('.') + 2);
700 int strW = (int) fm42.getStringBounds(s, g2).getWidth();
701 g2.drawString(s, d.width - strW - 9, fm42.getAscent());
702
703 int num = 30;
704 int progress = (int) (seconds / duration * num);
705 double ww = ((double) (d.width - 10) / (double) num);
706 double hh = (int) (d.height * 0.25);
707 double x = 0.0;
708 for (; x < progress; x += 1.0) {
709 g2.fill(new Rectangle2D.Double(x * ww + 5, d.height - hh - 5, ww - 1, hh));
710 }
711 g2.setColor(jfcDarkBlue);
712 for (; x < num; x += 1.0) {
713 g2.fill(new Rectangle2D.Double(x * ww + 5, d.height - hh - 5, ww - 1, hh));
714 }
715 }
716 }
717 }
718 }
719
720 public void start() {
721 pbThread = new Thread(this);
722 pbThread.setName("PlaybackMonitor");
723 pbThread.start();
724 }
725
726 public void stop() {
727 if (pbThread != null) {
728 pbThread.interrupt();
729 }
730 pbThread = null;
731 }
732
733 public void run() {
734 while (pbThread != null) {
735 try {
736 Thread.sleep(99);
737 } catch (Exception e) {
738 break;
739 }
740 repaint();
741 }
742 pbThread = null;
743 }
744 } // End PlaybackMonitor
745
746
747 /**
748 * Table to display the name of the sound.
749 */
750 class JukeTable extends JPanel implements ActionListener {
751
752 TableModel dataModel;
753 JFrame frame;
754 JTextField textField;
755 JButton applyB;
756
757 public JukeTable() {
758 setLayout(new BorderLayout());
759 setPreferredSize(new Dimension(260, 300));
760
761 final String[] names = {"#", "Name"};
762
763 dataModel = new AbstractTableModel() {
764 public int getColumnCount() {
765 return names.length;
766 }
767
768 public int getRowCount() {
769 return sounds.size();
770 }
771
772 public Object getValueAt(int row, int col) {
773 if (col == 0) {
774 return new Integer(row);
775 } else if (col == 1) {
776 Object object = sounds.get(row);
777 if (object instanceof File) {
778 return ((File) object).getName();
779 } else if (object instanceof URL) {
780 return ((URL) object).getFile();
781 }
782 }
783 return null;
784 }
785
786 public String getColumnName(int col) {
787 return names[col];
788 }
789
790 public Class getColumnClass(int c) {
791 return getValueAt(0, c).getClass();
792 }
793
794 public boolean isCellEditable(int row, int col) {
795 return false;
796 }
797
798 public void setValueAt(Object aValue, int row, int col) {
799 }
800 };
801
802 table = new JTable(dataModel);
803 TableColumn col = table.getColumn("#");
804 col.setMaxWidth(20);
805 table.sizeColumnsToFit(0);
806
807 JScrollPane scrollPane = new JScrollPane(table);
808 EmptyBorder eb = new EmptyBorder(5, 5, 2, 5);
809 scrollPane.setBorder(new CompoundBorder(eb, new EtchedBorder()));
810 add(scrollPane);
811
812 JPanel p1 = new JPanel();
813 JMenuBar menuBar = new JMenuBar();
814 menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));
815 JMenu menu = (JMenu) menuBar.add(new JMenu("Add"));
816 String items[] = {"File or Directory of Files", "URL"};
817 for (int i = 0; i < items.length; i++) {
818 JMenuItem item = menu.add(new JMenuItem(items[i]));
819 item.addActionListener(this);
820 }
821 p1.add(menuBar);
822
823 menuBar = new JMenuBar();
824 menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));
825 menu = (JMenu) menuBar.add(new JMenu("Remove"));
826 JMenuItem item = menu.add(new JMenuItem("Selected"));
827 item.addActionListener(this);
828 item = menu.add(new JMenuItem("All"));
829 item.addActionListener(this);
830 p1.add(menuBar);
831
832 loopB = addButton("loop", p1);
833 loopB.setBackground(Color.gray);
834 loopB.setSelected(true);
835
836 add("South", p1);
837 }
838
839
840 private JButton addButton(String name, JPanel p) {
841 JButton b = new JButton(name);
842 b.addActionListener(this);
843 p.add(b);
844 return b;
845 }
846
847
848 private void doFrame(String titleName) {
849 int w = 500;
850 int h = 130;
851 JPanel panel = new JPanel(new BorderLayout());
852 JPanel p1 = new JPanel();
853 if (titleName.endsWith("URL")) {
854 p1.add(new JLabel("URL :"));
855 textField = new JTextField("http://foo.bar.com/foo.wav");
856 textField.addActionListener(this);
857 } else {
858 p1.add(new JLabel("File or Dir :"));
859 String sep = String.valueOf(System.getProperty("file.separator").toCharArray()[0]);
860 String text = null;
861 try {
862 text = System.getProperty("user.dir") + sep;
863 } catch (SecurityException ex) {
864 reportStatus(ex.toString());
865 JavaSound.showInfoDialog();
866 return;
867 }
868 textField = new JTextField(text);
869 textField.setPreferredSize(new Dimension(w - 100, 30));
870 textField.addActionListener(this);
871 }
872 p1.add(textField);
873 panel.add(p1);
874 JPanel p2 = new JPanel();
875 applyB = addButton("Apply", p2);
876 addButton("Cancel", p2);
877 panel.add("South", p2);
878 frame = new JFrame(titleName);
879 frame.getContentPane().add("Center", panel);
880 frame.pack();
881 Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
882 frame.setLocation(d.width / 2 - w / 2, d.height / 2 - h / 2);
883 frame.setSize(w, h);
884 frame.setVisible(true);
885 }
886
887
888 public void actionPerformed(ActionEvent e) {
889 Object object = e.getSource();
890 if (object instanceof JTextField) {
891 applyB.doClick();
892 } else if (object instanceof JMenuItem) {
893 JMenuItem mi = (JMenuItem) object;
894 if (mi.getText().startsWith("File")) {
895 doFrame("Add File or Directory");
896 } else if (mi.getText().equals("URL")) {
897 doFrame("Add URL");
898 } else if (mi.getText().equals("Selected")) {
899 int rows[] = table.getSelectedRows();
900 Vector tmp = new Vector();
901 for (int i = 0; i < rows.length; i++) {
902 tmp.add(sounds.get(rows[i]));
903 }
904 sounds.removeAll(tmp);
905 tableChanged();
906 } else if (mi.getText().equals("All")) {
907 sounds.clear();
908 tableChanged();
909 }
910 } else if (object instanceof JButton) {
911 JButton button = (JButton) e.getSource();
912 if (button.getText().equals("Apply")) {
913 String name = textField.getText().trim();
914 if (name.startsWith("http") || name.startsWith("file")) {
915 try {
916 sounds.add(new URL(name));
917 } catch (Exception ex) {
918 ex.printStackTrace();
919 }
920 ;
921 } else {
922 loadJuke(name);
923 }
924 tableChanged();
925 } else if (button.getText().equals("Cancel")) {
926 frame.dispose();
927 frame = null;
928 errStr = null;
929 playbackMonitor.repaint();
930 } else if (button.getText().equals("loop")) {
931 loopB.setSelected(!loopB.isSelected());
932 loopB.setBackground(loopB.isSelected() ? Color.gray : Color.lightGray);
933 }
934 startB.setEnabled(sounds.size() != 0);
935 }
936 }
937
938 public void tableChanged() {
939 table.tableChanged(new TableModelEvent(dataModel));
940 }
941 } // End JukeTable
942
943
944 /**
945 * Animation thread for when an audio file loads.
946 */
947 class Loading extends Thread {
948
949 double extent;
950 int incr;
951
952 public void run() {
953 extent = 360.0;
954 incr = 10;
955 while (true) {
956 try {
957 sleep(99);
958 } catch (Exception ex) {
959 break;
960 }
961 playbackMonitor.repaint();
962 }
963 }
964
965 public void render(Dimension d, Graphics2D g2) {
966 if (isAlive()) {
967 FontRenderContext frc = g2.getFontRenderContext();
968 TextLayout tl = new TextLayout("Loading", g2.getFont(), frc);
969 float sw = (float) tl.getBounds().getWidth();
970 tl.draw(g2, d.width - sw - 45, d.height - 10);
971 double x = d.width - 33, y = d.height - 30, ew = 25, eh = 25;
972 g2.draw(new Ellipse2D.Double(x, y, ew, eh));
973 g2.fill(new Arc2D.Double(x, y, ew, eh, 90, extent, Arc2D.PIE));
974 if ((extent -= incr) < 0) {
975 extent = 350.0;
976 }
977 }
978 }
979 }
980
981
982 /**
983 * Animation thread for the contributors of Java Sound.
984 */
985 class Credits extends Thread {
986
987 int x;
988 Font font16 = new Font("serif", Font.PLAIN, 16);
989 String contributors = "Contributors : Kara Kytle, " +
990 "Jan Borgersen, " + "Brian Lichtenwalter";
991 int strWidth = getFontMetrics(font16).stringWidth(contributors);
992
993 public void run() {
994 x = -999;
995 while (!playbackMonitor.isShowing()) {
996 try {
997 sleep(999);
998 } catch (Exception e) {
999 return;
1000 }
1001 }
1002 for (int i = 0; i < 100; i++) {
1003 try {
1004 sleep(99);
1005 } catch (Exception e) {
1006 return;
1007 }
1008 }
1009 while (true) {
1010 if (--x < -strWidth) {
1011 x = playbackMonitor.getSize().width;
1012 }
1013 playbackMonitor.repaint();
1014 try {
1015 sleep(99);
1016 } catch (Exception ex) {
1017 break;
1018 }
1019 }
1020 }
1021
1022 public void render(Dimension d, Graphics2D g2) {
1023 if (isAlive()) {
1024 g2.setFont(font16);
1025 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1026 RenderingHints.VALUE_ANTIALIAS_OFF);
1027 g2.drawString(contributors, x, d.height - 5);
1028 }
1029 }
1030 }
1031
1032
1033 public static void main(String args[]) {
1034 String media = "media";
1035 final Juke juke = new Juke(args.length == 0 ? media : args[0]);
1036 juke.open();
1037 JFrame f = new JFrame("Juke Box");
1038 f.addWindowListener(new WindowAdapter() {
1039 public void windowClosing(WindowEvent e) {
1040 System.exit(0);
1041 }
1042
1043 public void windowIconified(WindowEvent e) {
1044 juke.credits.interrupt();
1045 }
1046 });
1047 f.getContentPane().add("Center", juke);
1048 f.pack();
1049 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
1050 int w = 750;
1051 int h = 340;
1052 f.setLocation(screenSize.width / 2 - w / 2, screenSize.height / 2 - h / 2);
1053 f.setSize(w, h);
1054 f.setVisible(true);
1055 if (args.length > 0) {
1056 File file = new File(args[0]);
1057 if (file == null && !file.isDirectory()) {
1058 System.out.println("usage: java Juke audioDirectory");
1059 }
1060 }
1061 }
1062 }
1063