/Users/lyon/j4p/src/sound/soundDemo/CapturePlaybackPanel.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.sampled.*; 
10   import javax.swing.*; 
11   import javax.swing.border.BevelBorder; 
12   import javax.swing.border.CompoundBorder; 
13   import javax.swing.border.EmptyBorder; 
14   import javax.swing.border.SoftBevelBorder; 
15   import java.awt.*; 
16   import java.awt.event.ActionEvent; 
17   import java.awt.event.ActionListener; 
18   import java.awt.event.WindowAdapter; 
19   import java.awt.event.WindowEvent; 
20   import java.awt.font.FontRenderContext; 
21   import java.awt.font.LineBreakMeasurer; 
22   import java.awt.font.TextAttribute; 
23   import java.awt.font.TextLayout; 
24   import java.awt.geom.Line2D; 
25   import java.io.ByteArrayInputStream; 
26   import java.io.ByteArrayOutputStream; 
27   import java.io.File; 
28   import java.io.IOException; 
29   import java.text.AttributedCharacterIterator; 
30   import java.text.AttributedString; 
31   import java.util.Enumeration; 
32   import java.util.Vector; 
33    
34    
35   /** 
36    *  Capture/Playback sample.  Record audio in different formats 
37    *  and then playback the recorded audio.  The captured audio can 
38    *  be saved either as a WAVE, AU or AIFF.  Or load an audio file 
39    *  for streaming playback. 
40    * 
41    * @version @(#)CapturePlaybackPanel.java   1.12    02/02/06 
42    * @author Brian Lichtenwalter 
43    */ 
44   public class CapturePlaybackPanel extends JPanel 
45           implements ActionListener, ControlContext { 
46    
47       private final int bufSize = 16384; 
48    
49       private FormatControls formatControls = new FormatControls(); 
50       private Capture capture = new Capture(); 
51       private Playback playback = new Playback(); 
52    
53       private AudioInputStream audioInputStream; 
54       private SamplingGraph samplingGraph; 
55    
56       private JButton playB, captB, pausB, loadB; 
57       private JButton auB, aiffB, waveB; 
58       private JTextField textField; 
59    
60       private String fileName = "untitled"; 
61       private String errStr; 
62       private double duration, seconds; 
63       private File file; 
64       private Vector graphicLineData = new Vector(); 
65    
66    
67       public CapturePlaybackPanel() { 
68           setLayout(new BorderLayout()); 
69           EmptyBorder eb = new EmptyBorder(5, 5, 5, 5); 
70           SoftBevelBorder sbb = new SoftBevelBorder(SoftBevelBorder.LOWERED); 
71           setBorder(new EmptyBorder(5, 5, 5, 5)); 
72    
73           JPanel p1 = new JPanel(); 
74           p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS)); 
75           p1.add(formatControls); 
76    
77           JPanel buttonPanel = new JPanel(); 
78           buttonPanel.setBorder(sbb); 
79           buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS)); 
80    
81           JPanel buttonsPanel = new JPanel(); 
82           buttonsPanel.setBorder(new EmptyBorder(10, 0, 5, 0)); 
83           playB = addButton("Play", buttonsPanel, false); 
84           captB = addButton("Record", buttonsPanel, true); 
85           pausB = addButton("Pause", buttonsPanel, false); 
86           loadB = addButton("Load...", buttonsPanel, true); 
87           buttonPanel.add(buttonsPanel); 
88    
89           JPanel samplingPanel = new JPanel(new BorderLayout()); 
90           eb = new EmptyBorder(10, 20, 20, 20); 
91           samplingPanel.setBorder(new CompoundBorder(eb, sbb)); 
92           samplingPanel.add(samplingGraph = new SamplingGraph()); 
93           buttonPanel.add(samplingPanel); 
94    
95           JPanel savePanel = new JPanel(); 
96           savePanel.setLayout(new BoxLayout(savePanel, BoxLayout.Y_AXIS)); 
97    
98           JPanel saveTFpanel = new JPanel(); 
99           saveTFpanel.add(new JLabel("File to save:  ")); 
100          saveTFpanel.add(textField = new JTextField(fileName)); 
101          textField.setPreferredSize(new Dimension(140, 25)); 
102          savePanel.add(saveTFpanel); 
103   
104          JPanel saveBpanel = new JPanel(); 
105          auB = addButton("Save AU", saveBpanel, false); 
106          aiffB = addButton("Save AIFF", saveBpanel, false); 
107          waveB = addButton("Save WAVE", saveBpanel, false); 
108          savePanel.add(saveBpanel); 
109   
110          buttonPanel.add(savePanel); 
111   
112          p1.add(buttonPanel); 
113          add(p1); 
114      } 
115   
116   
117      public void open() { 
118      } 
119   
120   
121      public void close() { 
122          if (playback.thread != null) { 
123              playB.doClick(0); 
124          } 
125          if (capture.thread != null) { 
126              captB.doClick(0); 
127          } 
128      } 
129   
130   
131      private JButton addButton(String name, JPanel p, boolean state) { 
132          JButton b = new JButton(name); 
133          b.addActionListener(this); 
134          b.setEnabled(state); 
135          p.add(b); 
136          return b; 
137      } 
138   
139   
140      public void actionPerformed(ActionEvent e) { 
141          Object obj = e.getSource(); 
142          if (obj.equals(auB)) { 
143              saveToFile(textField.getText().trim(), AudioFileFormat.Type.AU); 
144          } else if (obj.equals(aiffB)) { 
145              saveToFile(textField.getText().trim(), AudioFileFormat.Type.AIFF); 
146          } else if (obj.equals(waveB)) { 
147              saveToFile(textField.getText().trim(), AudioFileFormat.Type.WAVE); 
148          } else if (obj.equals(playB)) { 
149              if (playB.getText().startsWith("Play")) { 
150                  playback.start(); 
151                  samplingGraph.start(); 
152                  captB.setEnabled(false); 
153                  pausB.setEnabled(true); 
154                  playB.setText("Stop"); 
155              } else { 
156                  playback.stop(); 
157                  samplingGraph.stop(); 
158                  captB.setEnabled(true); 
159                  pausB.setEnabled(false); 
160                  playB.setText("Play"); 
161              } 
162          } else if (obj.equals(captB)) { 
163              if (captB.getText().startsWith("Record")) { 
164                  file = null; 
165                  capture.start(); 
166                  fileName = "untitled"; 
167                  samplingGraph.start(); 
168                  loadB.setEnabled(false); 
169                  playB.setEnabled(false); 
170                  pausB.setEnabled(true); 
171                  auB.setEnabled(false); 
172                  aiffB.setEnabled(false); 
173                  waveB.setEnabled(false); 
174                  captB.setText("Stop"); 
175              } else { 
176                  graphicLineData.removeAllElements(); 
177                  capture.stop(); 
178                  samplingGraph.stop(); 
179                  loadB.setEnabled(true); 
180                  playB.setEnabled(true); 
181                  pausB.setEnabled(false); 
182                  auB.setEnabled(true); 
183                  aiffB.setEnabled(true); 
184                  waveB.setEnabled(true); 
185                  captB.setText("Record"); 
186              } 
187          } else if (obj.equals(pausB)) { 
188              if (pausB.getText().startsWith("Pause")) { 
189                  if (capture.thread != null) { 
190                      capture.line.stop(); 
191                  } else { 
192                      if (playback.thread != null) { 
193                          playback.line.stop(); 
194                      } 
195                  } 
196                  pausB.setText("Resume"); 
197              } else { 
198                  if (capture.thread != null) { 
199                      capture.line.start(); 
200                  } else { 
201                      if (playback.thread != null) { 
202                          playback.line.start(); 
203                      } 
204                  } 
205                  pausB.setText("Pause"); 
206              } 
207          } else if (obj.equals(loadB)) { 
208              try { 
209                  File file = new File(System.getProperty("user.dir")); 
210                  JFileChooser fc = new JFileChooser(file); 
211                  fc.setFileFilter(new javax.swing.filechooser.FileFilter() { 
212                      public boolean accept(File f) { 
213                          if (f.isDirectory()) { 
214                              return true; 
215                          } 
216                          String name = f.getName(); 
217                          if (name.endsWith(".au") || name.endsWith(".wav") || name.endsWith(".aiff") || name.endsWith(".aif")) { 
218                              return true; 
219                          } 
220                          return false; 
221                      } 
222   
223                      public String getDescription() { 
224                          return ".au, .wav, .aif"; 
225                      } 
226                  }); 
227   
228                  if (fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { 
229                      createAudioInputStream(fc.getSelectedFile(), true); 
230                  } 
231              } catch (SecurityException ex) { 
232                  JavaSound.showInfoDialog(); 
233                  ex.printStackTrace(); 
234              } catch (Exception ex) { 
235                  ex.printStackTrace(); 
236              } 
237          } 
238      } 
239   
240   
241      public void createAudioInputStream(File file, boolean updateComponents) { 
242          if (file != null && file.isFile()) { 
243              try { 
244                  this.file = file; 
245                  errStr = null; 
246                  audioInputStream = AudioSystem.getAudioInputStream(file); 
247                  playB.setEnabled(true); 
248                  fileName = file.getName(); 
249                  long milliseconds = (long) ((audioInputStream.getFrameLength() * 1000) / audioInputStream.getFormat().getFrameRate()); 
250                  duration = milliseconds / 1000.0; 
251                  auB.setEnabled(true); 
252                  aiffB.setEnabled(true); 
253                  waveB.setEnabled(true); 
254                  if (updateComponents) { 
255                      formatControls.setFormat(audioInputStream.getFormat()); 
256                      samplingGraph.createWaveForm(null); 
257                  } 
258              } catch (Exception ex) { 
259                  reportStatus(ex.toString()); 
260              } 
261          } else { 
262              reportStatus("Audio file required."); 
263          } 
264      } 
265   
266   
267      public void saveToFile(String name, AudioFileFormat.Type fileType) { 
268   
269          if (audioInputStream == null) { 
270              reportStatus("No loaded audio to save"); 
271              return; 
272          } else if (file != null) { 
273              createAudioInputStream(file, false); 
274          } 
275   
276          // reset to the beginnning of the captured data 
277          try { 
278              audioInputStream.reset(); 
279          } catch (Exception e) { 
280              reportStatus("Unable to reset stream " + e); 
281              return; 
282          } 
283   
284          File file = new File(fileName = name); 
285          try { 
286              if (AudioSystem.write(audioInputStream, fileType, file) == -1) { 
287                  throw new IOException("Problems writing to file"); 
288              } 
289          } catch (Exception ex) { 
290              reportStatus(ex.toString()); 
291          } 
292          samplingGraph.repaint(); 
293      } 
294   
295   
296      private void reportStatus(String msg) { 
297          if ((errStr = msg) != null) { 
298              System.out.println(errStr); 
299              samplingGraph.repaint(); 
300          } 
301      } 
302   
303   
304      /** 
305       * Write data to the OutputChannel. 
306       */ 
307      public class Playback implements Runnable { 
308   
309          SourceDataLine line; 
310          Thread thread; 
311   
312          public void start() { 
313              errStr = null; 
314              thread = new Thread(this); 
315              thread.setName("Playback"); 
316              thread.start(); 
317          } 
318   
319          public void stop() { 
320              thread = null; 
321          } 
322   
323          private void shutDown(String message) { 
324              if ((errStr = message) != null) { 
325                  System.err.println(errStr); 
326                  samplingGraph.repaint(); 
327              } 
328              if (thread != null) { 
329                  thread = null; 
330                  samplingGraph.stop(); 
331                  captB.setEnabled(true); 
332                  pausB.setEnabled(false); 
333                  playB.setText("Play"); 
334              } 
335          } 
336   
337          public void run() { 
338   
339              // reload the file if loaded by file 
340              if (file != null) { 
341                  createAudioInputStream(file, false); 
342              } 
343   
344              // make sure we have something to play 
345              if (audioInputStream == null) { 
346                  shutDown("No loaded audio to play back"); 
347                  return; 
348              } 
349              // reset to the beginnning of the stream 
350              try { 
351                  audioInputStream.reset(); 
352              } catch (Exception e) { 
353                  shutDown("Unable to reset the stream\n" + e); 
354                  return; 
355              } 
356   
357              // get an AudioInputStream of the desired format for playback 
358              AudioFormat format = formatControls.getFormat(); 
359              AudioInputStream playbackInputStream = AudioSystem.getAudioInputStream(format, audioInputStream); 
360   
361              if (playbackInputStream == null) { 
362                  shutDown("Unable to convert stream of format " + audioInputStream + " to format " + format); 
363                  return; 
364              } 
365   
366              // define the required attributes for our line, 
367              // and make sure a compatible line is supported. 
368   
369              DataLine.Info info = new DataLine.Info(SourceDataLine.class, 
370                      format); 
371              if (!AudioSystem.isLineSupported(info)) { 
372                  shutDown("Line matching " + info + " not supported."); 
373                  return; 
374              } 
375   
376              // get and open the source data line for playback. 
377   
378              try { 
379                  line = (SourceDataLine) AudioSystem.getLine(info); 
380                  line.open(format, bufSize); 
381              } catch (LineUnavailableException ex) { 
382                  shutDown("Unable to open the line: " + ex); 
383                  return; 
384              } 
385   
386              // play back the captured audio data 
387   
388              int frameSizeInBytes = format.getFrameSize(); 
389              int bufferLengthInFrames = line.getBufferSize() / 8; 
390              int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes; 
391              byte[] data = new byte[bufferLengthInBytes]; 
392              int numBytesRead = 0; 
393   
394              // start the source data line 
395              line.start(); 
396   
397              while (thread != null) { 
398                  try { 
399                      if ((numBytesRead = playbackInputStream.read(data)) == -1) { 
400                          break; 
401                      } 
402                      int numBytesRemaining = numBytesRead; 
403                      while (numBytesRemaining > 0) { 
404                          numBytesRemaining -= line.write(data, 0, numBytesRemaining); 
405                      } 
406                  } catch (Exception e) { 
407                      shutDown("Error during playback: " + e); 
408                      break; 
409                  } 
410              } 
411              // we reached the end of the stream.  let the data play out, then 
412              // stop and close the line. 
413              if (thread != null) { 
414                  line.drain(); 
415              } 
416              line.stop(); 
417              line.close(); 
418              line = null; 
419              shutDown(null); 
420          } 
421      } // End class Playback 
422   
423   
424      /** 
425       * Reads data from the input channel and writes to the output stream 
426       */ 
427      class Capture implements Runnable { 
428   
429          TargetDataLine line; 
430          Thread thread; 
431   
432          public void start() { 
433              errStr = null; 
434              thread = new Thread(this); 
435              thread.setName("Capture"); 
436              thread.start(); 
437          } 
438   
439          public void stop() { 
440              thread = null; 
441          } 
442   
443          private void shutDown(String message) { 
444              if ((errStr = message) != null && thread != null) { 
445                  thread = null; 
446                  samplingGraph.stop(); 
447                  loadB.setEnabled(true); 
448                  playB.setEnabled(true); 
449                  pausB.setEnabled(false); 
450                  auB.setEnabled(true); 
451                  aiffB.setEnabled(true); 
452                  waveB.setEnabled(true); 
453                  captB.setText("Record"); 
454                  System.err.println(errStr); 
455                  samplingGraph.repaint(); 
456              } 
457          } 
458   
459          public void run() { 
460   
461              duration = 0; 
462              audioInputStream = null; 
463   
464              // define the required attributes for our line, 
465              // and make sure a compatible line is supported. 
466   
467              AudioFormat format = formatControls.getFormat(); 
468              DataLine.Info info = new DataLine.Info(TargetDataLine.class, 
469                      format); 
470   
471              if (!AudioSystem.isLineSupported(info)) { 
472                  shutDown("Line matching " + info + " not supported."); 
473                  return; 
474              } 
475   
476              // get and open the target data line for capture. 
477   
478              try { 
479                  line = (TargetDataLine) AudioSystem.getLine(info); 
480                  line.open(format, line.getBufferSize()); 
481              } catch (LineUnavailableException ex) { 
482                  shutDown("Unable to open the line: " + ex); 
483                  return; 
484              } catch (SecurityException ex) { 
485                  shutDown(ex.toString()); 
486                  JavaSound.showInfoDialog(); 
487                  return; 
488              } catch (Exception ex) { 
489                  shutDown(ex.toString()); 
490                  return; 
491              } 
492   
493              // play back the captured audio data 
494              ByteArrayOutputStream out = new ByteArrayOutputStream(); 
495              int frameSizeInBytes = format.getFrameSize(); 
496              int bufferLengthInFrames = line.getBufferSize() / 8; 
497              int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes; 
498              byte[] data = new byte[bufferLengthInBytes]; 
499              int numBytesRead; 
500   
501              line.start(); 
502   
503              while (thread != null) { 
504                  if ((numBytesRead = line.read(data, 0, bufferLengthInBytes)) == -1) { 
505                      break; 
506                  } 
507                  out.write(data, 0, numBytesRead); 
508              } 
509   
510              // we reached the end of the stream.  stop and close the line. 
511              line.stop(); 
512              line.close(); 
513              line = null; 
514   
515              // stop and close the output stream 
516              try { 
517                  out.flush(); 
518                  out.close(); 
519              } catch (IOException ex) { 
520                  ex.printStackTrace(); 
521              } 
522   
523              // load bytes into the audio input stream for playback 
524   
525              byte audioBytes[] = out.toByteArray(); 
526              ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes); 
527              audioInputStream = new AudioInputStream(bais, format, audioBytes.length / frameSizeInBytes); 
528   
529              long milliseconds = (long) ((audioInputStream.getFrameLength() * 1000) / format.getFrameRate()); 
530              duration = milliseconds / 1000.0; 
531   
532              try { 
533                  audioInputStream.reset(); 
534              } catch (Exception ex) { 
535                  ex.printStackTrace(); 
536                  return; 
537              } 
538   
539              samplingGraph.createWaveForm(audioBytes); 
540          } 
541      } // End class Capture 
542   
543   
544      /** 
545       * Controls for the AudioFormat. 
546       */ 
547      class FormatControls extends JPanel { 
548   
549          Vector groups = new Vector(); 
550          JToggleButton linrB, ulawB, alawB, rate8B, rate11B, rate16B, rate22B, rate44B; 
551          JToggleButton size8B, size16B, signB, unsignB, litB, bigB, monoB,sterB; 
552   
553          public FormatControls() { 
554              setLayout(new GridLayout(0, 1)); 
555              EmptyBorder eb = new EmptyBorder(0, 0, 0, 5); 
556              BevelBorder bb = new BevelBorder(BevelBorder.LOWERED); 
557              CompoundBorder cb = new CompoundBorder(eb, bb); 
558              setBorder(new CompoundBorder(cb, new EmptyBorder(8, 5, 5, 5))); 
559              JPanel p1 = new JPanel(); 
560              ButtonGroup encodingGroup = new ButtonGroup(); 
561              linrB = addToggleButton(p1, encodingGroup, "linear", true); 
562              ulawB = addToggleButton(p1, encodingGroup, "ulaw", false); 
563              alawB = addToggleButton(p1, encodingGroup, "alaw", false); 
564              add(p1); 
565              groups.addElement(encodingGroup); 
566   
567              JPanel p2 = new JPanel(); 
568              JPanel p2b = new JPanel(); 
569              ButtonGroup sampleRateGroup = new ButtonGroup(); 
570              rate8B = addToggleButton(p2, sampleRateGroup, "8000", false); 
571              rate11B = addToggleButton(p2, sampleRateGroup, "11025", false); 
572              rate16B = addToggleButton(p2b, sampleRateGroup, "16000", false); 
573              rate22B = addToggleButton(p2b, sampleRateGroup, "22050", false); 
574              rate44B = addToggleButton(p2b, sampleRateGroup, "44100", true); 
575              add(p2); 
576              add(p2b); 
577              groups.addElement(sampleRateGroup); 
578   
579              JPanel p3 = new JPanel(); 
580              ButtonGroup sampleSizeInBitsGroup = new ButtonGroup(); 
581              size8B = addToggleButton(p3, sampleSizeInBitsGroup, "8", false); 
582              size16B = addToggleButton(p3, sampleSizeInBitsGroup, "16", true); 
583              add(p3); 
584              groups.addElement(sampleSizeInBitsGroup); 
585   
586              JPanel p4 = new JPanel(); 
587              ButtonGroup signGroup = new ButtonGroup(); 
588              signB = addToggleButton(p4, signGroup, "signed", true); 
589              unsignB = addToggleButton(p4, signGroup, "unsigned", false); 
590              add(p4); 
591              groups.addElement(signGroup); 
592   
593              JPanel p5 = new JPanel(); 
594              ButtonGroup endianGroup = new ButtonGroup(); 
595              litB = addToggleButton(p5, endianGroup, "little endian", false); 
596              bigB = addToggleButton(p5, endianGroup, "big endian", true); 
597              add(p5); 
598              groups.addElement(endianGroup); 
599   
600              JPanel p6 = new JPanel(); 
601              ButtonGroup channelsGroup = new ButtonGroup(); 
602              monoB = addToggleButton(p6, channelsGroup, "mono", false); 
603              sterB = addToggleButton(p6, channelsGroup, "stereo", true); 
604              add(p6); 
605              groups.addElement(channelsGroup); 
606          } 
607   
608          private JToggleButton addToggleButton(JPanel p, ButtonGroup g, 
609                                                String name, boolean state) { 
610              JToggleButton b = new JToggleButton(name, state); 
611              p.add(b); 
612              g.add(b); 
613              return b; 
614          } 
615   
616          public AudioFormat getFormat() { 
617   
618              Vector v = new Vector(groups.size()); 
619              for (int i = 0; i < groups.size(); i++) { 
620                  ButtonGroup g = (ButtonGroup) groups.get(i); 
621                  for (Enumeration e = g.getElements(); e.hasMoreElements();) { 
622                      AbstractButton b = (AbstractButton) e.nextElement(); 
623                      if (b.isSelected()) { 
624                          v.add(b.getText()); 
625                          break; 
626                      } 
627                  } 
628              } 
629   
630              AudioFormat.Encoding encoding = AudioFormat.Encoding.ULAW; 
631              String encString = (String) v.get(0); 
632              float rate = Float.valueOf((String) v.get(1)).floatValue(); 
633              int sampleSize = Integer.valueOf((String) v.get(2)).intValue(); 
634              String signedString = (String) v.get(3); 
635              boolean bigEndian = ((String) v.get(4)).startsWith("big"); 
636              int channels = (v.get(5)).equals("mono") ? 1 : 2; 
637   
638              if (encString.equals("linear")) { 
639                  if (signedString.equals("signed")) { 
640                      encoding = AudioFormat.Encoding.PCM_SIGNED; 
641                  } else { 
642                      encoding = AudioFormat.Encoding.PCM_UNSIGNED; 
643                  } 
644              } else if (encString.equals("alaw")) { 
645                  encoding = AudioFormat.Encoding.ALAW; 
646              } 
647              return new AudioFormat(encoding, rate, sampleSize, 
648                      channels, (sampleSize / 8) * channels, rate, bigEndian); 
649          } 
650   
651   
652          public void setFormat(AudioFormat format) { 
653              AudioFormat.Encoding type = format.getEncoding(); 
654              if (type == AudioFormat.Encoding.ULAW) { 
655                  ulawB.doClick(); 
656              } else if (type == AudioFormat.Encoding.ALAW) { 
657                  alawB.doClick(); 
658              } else if (type == AudioFormat.Encoding.PCM_SIGNED) { 
659                  linrB.doClick(); 
660                  signB.doClick(); 
661              } else if (type == AudioFormat.Encoding.PCM_UNSIGNED) { 
662                  linrB.doClick(); 
663                  unsignB.doClick(); 
664              } 
665              float rate = format.getFrameRate(); 
666              if (rate == 8000) { 
667                  rate8B.doClick(); 
668              } else if (rate == 11025) { 
669                  rate11B.doClick(); 
670              } else if (rate == 16000) { 
671                  rate16B.doClick(); 
672              } else if (rate == 22050) { 
673                  rate22B.doClick(); 
674              } else if (rate == 44100) { 
675                  rate44B.doClick(); 
676              } 
677              switch (format.getSampleSizeInBits()) { 
678                  case 8: 
679                      size8B.doClick(); 
680                      break; 
681                  case 16: 
682                      size16B.doClick(); 
683                      break; 
684              } 
685              if (format.isBigEndian()) { 
686                  bigB.doClick(); 
687              } else { 
688                  litB.doClick(); 
689              } 
690              if (format.getChannels() == 1) { 
691                  monoB.doClick(); 
692              } else { 
693                  sterB.doClick(); 
694              } 
695          } 
696      } // End class FormatControls 
697   
698      /** 
699       * Render a WaveForm. 
700       */ 
701      class SamplingGraph extends JPanel implements Runnable { 
702   
703          private Thread thread; 
704          //private Font font10 = new Font("serif", Font.PLAIN, 10); 
705          private Font font12 = new Font("serif", Font.PLAIN, 12); 
706          Color jfcBlue = new Color(204, 204, 255); 
707          Color pink = new Color(255, 175, 175); 
708   
709   
710          public SamplingGraph() { 
711              setBackground(new Color(20, 20, 20)); 
712          } 
713   
714   
715          public void createWaveForm(byte[] audioBytes) { 
716   
717              graphicLineData.removeAllElements();  // clear the old vector 
718   
719              AudioFormat format = audioInputStream.getFormat(); 
720              if (audioBytes == null) { 
721                  try { 
722                      audioBytes = new byte[ 
723                              (int) (audioInputStream.getFrameLength() 
724                              * format.getFrameSize())]; 
725                      audioInputStream.read(audioBytes); 
726                  } catch (Exception ex) { 
727                      reportStatus(ex.toString()); 
728                      return; 
729                  } 
730              } 
731   
732              Dimension d = getSize(); 
733              int w = d.width; 
734              int h = d.height - 15; 
735              int[] audioData = null; 
736              audioData = getAudioData(format, audioBytes, audioData); 
737              int frames_per_pixel = audioBytes.length / format.getFrameSize() / w; 
738              byte my_byte = 0; 
739              double y_last = 0; 
740              int numChannels = format.getChannels(); 
741              for (double x = 0; x < w && audioData != null; x++) { 
742                  int idx = (int) (frames_per_pixel * numChannels * x); 
743                  if (format.getSampleSizeInBits() == 8) { 
744                      my_byte = (byte) audioData[idx]; 
745                  } else { 
746                      my_byte = (byte) (128 * audioData[idx] / 32768); 
747                  } 
748                  double y_new = (double) (h * (128 - my_byte) / 256); 
749                  graphicLineData.add(new Line2D.Double(x, y_last, x, y_new)); 
750                  y_last = y_new; 
751              } 
752   
753              repaint(); 
754          } 
755   
756          private int[] getAudioData(AudioFormat format, byte[] audioBytes, int[] audioData) { 
757              if (format.getSampleSizeInBits() == 16) { 
758                  int nlengthInSamples = audioBytes.length / 2; 
759                  audioData = new int[nlengthInSamples]; 
760                  if (format.isBigEndian()) { 
761                      for (int i = 0; i < nlengthInSamples; i++) { 
762                          /* First byte is MSB (high order) */ 
763                          int MSB = (int) audioBytes[2 * i]; 
764                          /* Second byte is LSB (low order) */ 
765                          int LSB = (int) audioBytes[2 * i + 1]; 
766                          audioData[i] = MSB << 8 | (255 & LSB); 
767                      } 
768                  } else { 
769                      for (int i = 0; i < nlengthInSamples; i++) { 
770                          /* First byte is LSB (low order) */ 
771                          int LSB = (int) audioBytes[2 * i]; 
772                          /* Second byte is MSB (high order) */ 
773                          int MSB = (int) audioBytes[2 * i + 1]; 
774                          audioData[i] = MSB << 8 | (255 & LSB); 
775                      } 
776                  } 
777              } else if (format.getSampleSizeInBits() == 8) { 
778                  int nlengthInSamples = audioBytes.length; 
779                  audioData = new int[nlengthInSamples]; 
780                  if (format.getEncoding().toString().startsWith("PCM_SIGN")) { 
781                      for (int i = 0; i < audioBytes.length; i++) { 
782                          audioData[i] = audioBytes[i]; 
783                      } 
784                  } else { 
785                      for (int i = 0; i < audioBytes.length; i++) { 
786                          audioData[i] = audioBytes[i] - 128; 
787                      } 
788                  } 
789              } 
790              return audioData; 
791          } 
792   
793          public void paint(Graphics g) { 
794   
795              Dimension d = getSize(); 
796              int w = d.width; 
797              int h = d.height; 
798              int INFOPAD = 15; 
799   
800              Graphics2D g2 = (Graphics2D) g; 
801              g2.setBackground(getBackground()); 
802              g2.clearRect(0, 0, w, h); 
803              g2.setColor(Color.white); 
804              g2.fillRect(0, h - INFOPAD, w, INFOPAD); 
805   
806              if (errStr != null) { 
807                  g2.setColor(jfcBlue); 
808                  g2.setFont(new Font("serif", Font.BOLD, 18)); 
809                  g2.drawString("ERROR", 5, 20); 
810                  AttributedString as = new AttributedString(errStr); 
811                  as.addAttribute(TextAttribute.FONT, font12, 0, errStr.length()); 
812                  AttributedCharacterIterator aci = as.getIterator(); 
813                  FontRenderContext frc = g2.getFontRenderContext(); 
814                  LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc); 
815                  float x = 5, y = 25; 
816                  lbm.setPosition(0); 
817                  while (lbm.getPosition() < errStr.length()) { 
818                      TextLayout tl = lbm.nextLayout(w - x - 5); 
819                      if (!tl.isLeftToRight()) { 
820                          x = w - tl.getAdvance(); 
821                      } 
822                      tl.draw(g2, x, y += tl.getAscent()); 
823                      y += tl.getDescent() + tl.getLeading(); 
824                  } 
825              } else if (capture.thread != null) { 
826                  g2.setColor(Color.black); 
827                  g2.setFont(font12); 
828                  g2.drawString("Length: " + String.valueOf(seconds), 3, h - 4); 
829              } else { 
830                  g2.setColor(Color.black); 
831                  g2.setFont(font12); 
832                  g2.drawString("File: " + fileName + "  Length: " + String.valueOf(duration) + "  Position: " + String.valueOf(seconds), 3, h - 4); 
833                  drawData2(g2); 
834              } 
835          } 
836   
837          private void drawData2(Graphics2D g2) { 
838              if (audioInputStream != null) { 
839                  // .. render sampling graph .. 
840                  g2.setColor(jfcBlue); 
841                  for (int i = 1; i < graphicLineData.size(); i++) { 
842                      g2.draw((Line2D) graphicLineData.get(i)); 
843                  } 
844                  //drawData(w, g2, h, INFOPAD); 
845              } 
846          } 
847   
848          private void drawData(int w, Graphics2D g2, int h, int INFOPAD) { 
849              // .. draw current position .. 
850              if (seconds != 0) { 
851                  double loc = seconds / duration * w; 
852                  g2.setColor(pink); 
853                  g2.setStroke(new BasicStroke(3)); 
854                  g2.draw(new Line2D.Double(loc, 0, loc, h - INFOPAD - 2)); 
855              } 
856          } 
857   
858          public void start() { 
859              thread = new Thread(this); 
860              thread.setName("SamplingGraph"); 
861              thread.start(); 
862              seconds = 0; 
863          } 
864   
865          public void stop() { 
866              if (thread != null) { 
867                  thread.interrupt(); 
868              } 
869              thread = null; 
870          } 
871   
872          public void run() { 
873              seconds = 0; 
874              while (thread != null) { 
875                  if ((playback.line != null) && (playback.line.isOpen())) { 
876   
877                      long milliseconds = (playback.line.getMicrosecondPosition() / 1000); 
878                      seconds = milliseconds / 1000.0; 
879                  } else if ((capture.line != null) && (capture.line.isActive())) { 
880   
881                      long milliseconds = (capture.line.getMicrosecondPosition() / 1000); 
882                      seconds = milliseconds / 1000.0; 
883                  } 
884   
885                  try { 
886                      Thread.sleep(100); 
887                  } catch (Exception e) { 
888                      break; 
889                  } 
890   
891                  repaint(); 
892   
893                  while ((capture.line != null && !capture.line.isActive()) || 
894                          (playback.line != null && !playback.line.isOpen())) { 
895                      try { 
896                          Thread.sleep(10); 
897                      } catch (Exception e) { 
898                          break; 
899                      } 
900                  } 
901              } 
902              seconds = 0; 
903              repaint(); 
904          } 
905      } // End class SamplingGraph 
906   
907   
908      public static void main(String s[]) { 
909          CapturePlaybackPanel capturePlaybackPanel = new CapturePlaybackPanel(); 
910          capturePlaybackPanel.open(); 
911          JFrame f = new JFrame("Capture/Playback"); 
912          f.addWindowListener(new WindowAdapter() { 
913              public void windowClosing(WindowEvent e) { 
914                  System.exit(0); 
915              } 
916          }); 
917          f.getContentPane().add("Center", capturePlaybackPanel); 
918          f.pack(); 
919          Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 
920          int w = 720; 
921          int h = 340; 
922          f.setLocation(screenSize.width / 2 - w / 2, screenSize.height / 2 - h / 2); 
923          f.setSize(w, h); 
924          f.show(); 
925      } 
926   
927      public int getBufSize() { 
928          return bufSize; 
929      } 
930   
931      public FormatControls getFormatControls() { 
932          return formatControls; 
933      } 
934   
935      public Capture getCapture() { 
936          return capture; 
937      } 
938   
939      public Playback getPlayback() { 
940          return playback; 
941      } 
942   
943      public AudioInputStream getAudioInputStream() { 
944          return audioInputStream; 
945      } 
946   
947      public SamplingGraph getSamplingGraph() { 
948          return samplingGraph; 
949      } 
950   
951      public JButton getPlayB() { 
952          return playB; 
953      } 
954   
955      public JButton getCaptB() { 
956          return captB; 
957      } 
958   
959      public JButton getPausB() { 
960          return pausB; 
961      } 
962   
963      public JButton getLoadB() { 
964          return loadB; 
965      } 
966   
967      public JButton getAuB() { 
968          return auB; 
969      } 
970   
971      public JButton getAiffB() { 
972          return aiffB; 
973      } 
974   
975      public JButton getWaveB() { 
976          return waveB; 
977      } 
978   
979      public JTextField getTextField() { 
980          return textField; 
981      } 
982   
983      public String getFileName() { 
984          return fileName; 
985      } 
986   
987      public String getErrStr() { 
988          return errStr; 
989      } 
990   
991      public double getDuration() { 
992          return duration; 
993      } 
994   
995      public double getSeconds() { 
996          return seconds; 
997      } 
998   
999      public File getFile() { 
1000         return file; 
1001     } 
1002  
1003     public Vector getGraphicLineData() { 
1004         return graphicLineData; 
1005     } 
1006 } 
1007