/Users/lyon/j4p/src/sound/audioDigitizer/CapturePlayBackPanel.java

1    package sound.audioDigitizer; 
2     
3    import sound.soundDemo.ControlContext; 
4    import sound.soundDemo.JavaSound; 
5    import sound.OscopePanel; 
6     
7    import javax.sound.sampled.AudioFormat; 
8    import javax.sound.sampled.AudioInputStream; 
9    import javax.sound.sampled.AudioSystem; 
10   import javax.sound.sampled.DataLine; 
11   import javax.sound.sampled.LineUnavailableException; 
12   import javax.sound.sampled.SourceDataLine; 
13   import javax.sound.sampled.TargetDataLine; 
14   import javax.swing.AbstractButton; 
15   import javax.swing.ButtonGroup; 
16   import javax.swing.JButton; 
17   import javax.swing.JPanel; 
18   import javax.swing.JToggleButton; 
19   import javax.swing.border.CompoundBorder; 
20   import javax.swing.border.EmptyBorder; 
21   import java.awt.BorderLayout; 
22   import java.awt.Container; 
23   import java.awt.FlowLayout; 
24   import java.awt.GridLayout; 
25   import java.awt.event.ActionEvent; 
26   import java.awt.event.ActionListener; 
27   import java.io.ByteArrayInputStream; 
28   import java.io.ByteArrayOutputStream; 
29   import java.io.IOException; 
30   import java.util.Enumeration; 
31   import java.util.Vector; 
32    
33   public class CapturePlayBackPanel extends JPanel 
34           implements ActionListener, ControlContext { 
35       private final int bufSize = 16384; 
36       private FormatControls formatControls = new FormatControls(); 
37       private Capture capture = new Capture(); 
38       private Playback playback = new Playback(); 
39       private int[] audioData = null; 
40       private OscopePanel dp = new OscopePanel(); 
41       private AudioInputStream audioInputStream; 
42       private JButton playB, captB; 
43       private String errStr; 
44    
45       public CapturePlayBackPanel() { 
46           setLayout(new BorderLayout()); 
47           double d[] = {0.0,0.0}; 
48           dp.setData(d); 
49           add(dp, BorderLayout.CENTER); 
50           add(getButtons(), BorderLayout.SOUTH); 
51       } 
52    
53       public JPanel getButtons() { 
54           JPanel buttonsPanel = new JPanel(); 
55           buttonsPanel.setLayout(new FlowLayout()); 
56           playB = addButton("Play", buttonsPanel, false); 
57           captB = addButton("Record", buttonsPanel, true); 
58           return buttonsPanel; 
59       } 
60    
61       public void open() { 
62       } 
63    
64       public void close() { 
65           if (playback.thread != null) { 
66               playB.doClick(0); 
67           } 
68           if (capture.thread != null) { 
69               captB.doClick(0); 
70           } 
71       } 
72    
73       private JButton addButton(String name, JPanel p, boolean state) { 
74           JButton b = new JButton(name); 
75           b.addActionListener(this); 
76           b.setEnabled(state); 
77           p.add(b); 
78           return b; 
79       } 
80    
81       //this responds to user's input 
82       public void actionPerformed(ActionEvent e) { 
83           Object obj = e.getSource(); 
84           if (obj.equals(playB)) { 
85               if (playB.getText().startsWith("Play")) { 
86                   playback.start(); 
87                   captB.setEnabled(false); 
88                   playB.setText("Stop"); 
89               } else { 
90                   playback.stop(); 
91                   captB.setEnabled(true); 
92                   playB.setText("Play"); 
93               } 
94           } else if (obj.equals(captB)) { 
95               if (captB.getText().startsWith("Record")) { 
96                   capture.start(); 
97                   playB.setEnabled(false); 
98                   captB.setText("Stop"); 
99               } else { 
100                  capture.stop(); 
101                  playB.setEnabled(true); 
102                  captB.setText("Record"); 
103              } 
104          } 
105      } 
106      /** 
107       * This is a method that alters its parameters. 
108       * @param audioBytes 
109       */ 
110      public byte[] createWaveForm() { 
111          AudioFormat format = audioInputStream.getFormat(); 
112          byte[] audioBytes = new byte[ 
113                          (int) (audioInputStream.getFrameLength() 
114                          * format.getFrameSize())]; 
115          try { 
116              audioInputStream.read(audioBytes); 
117          } catch (IOException e) { 
118              e.printStackTrace(); 
119          } 
120          if (format.getSampleSizeInBits() == 16) { 
121              int nlengthInSamples = audioBytes.length / 2; 
122              audioData = new int[nlengthInSamples]; 
123              if (format.isBigEndian()) { 
124                  for (int i = 0; i < nlengthInSamples; i++) { 
125                      /* First byte is MSB (high order) */ 
126                      int MSB = (int) audioBytes[2 * i]; 
127                      /* Second byte is LSB (low order) */ 
128                      int LSB = (int) audioBytes[2 * i + 1]; 
129                      audioData[i] = MSB << 8 | (255 & LSB); 
130                  } 
131              } else { 
132                  for (int i = 0; i < nlengthInSamples; i++) { 
133                      /* First byte is LSB (low order) */ 
134                      int LSB = (int) audioBytes[2 * i]; 
135                      /* Second byte is MSB (high order) */ 
136                      int MSB = (int) audioBytes[2 * i + 1]; 
137                      audioData[i] = MSB << 8 | (255 & LSB); 
138                  } 
139              } 
140          } else if (format.getSampleSizeInBits() == 8) { 
141              int nlengthInSamples = audioBytes.length; 
142              audioData = new int[nlengthInSamples]; 
143              if (format.getEncoding().toString().startsWith("PCM_SIGN")) { 
144                  for (int i = 0; i < audioBytes.length; i++) { 
145                      audioData[i] = audioBytes[i]; 
146                  } 
147              } else { 
148                  for (int i = 0; i < audioBytes.length; i++) { 
149                      audioData[i] = audioBytes[i] - 128; 
150                  } 
151              } 
152          } 
153          dp.setData(getDoubleData(audioData)); 
154          dp.repaint(500); 
155          return audioBytes; 
156      } 
157   
158      // this converts the wave array into an a array of doubles 
159      private double[] getDoubleData(int data[]) { 
160          double[] data2 = new double[data.length]; 
161          for (int i = 0; i < data.length; i++) { 
162              data2[i] = (double) data[i] / 32768.0; 
163          } 
164          return data2; 
165      } 
166   
167      private void reportStatus(String msg) { 
168          if ((errStr = msg) != null) { 
169              System.out.println(errStr); 
170          } 
171      } 
172   
173      /** 
174       * Write data to the OutputChannel. 
175       */ 
176      public class Playback implements Runnable { 
177          SourceDataLine line; 
178          Thread thread; 
179   
180          public void start() { 
181              errStr = null; 
182              thread = new Thread(this); 
183              thread.setName("Playback"); 
184              thread.start(); 
185          } 
186   
187          public void stop() { 
188              thread = null; 
189          } 
190   
191          private void shutDown(String message) { 
192              if ((errStr = message) != null) { 
193                  System.err.println(errStr); 
194              } 
195              if (thread != null) { 
196                  thread = null; 
197                  captB.setEnabled(true); 
198                  playB.setText("Play"); 
199              } 
200          } 
201   
202          public void run() { 
203              // make sure we have something to play 
204              if (audioInputStream == null) { 
205                  shutDown("No loaded audio to play back"); 
206                  return; 
207              } 
208              // reset to the beginnning of the stream 
209              try { 
210                  audioInputStream.reset(); 
211              } catch (Exception e) { 
212                  shutDown("Unable to reset the stream\n" + e); 
213                  return; 
214              } 
215   
216              // get an AudioInputStream of the desired format for playback 
217              AudioFormat format = formatControls.getFormat(); 
218              AudioInputStream playbackInputStream = AudioSystem.getAudioInputStream(format, audioInputStream); 
219              DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); 
220              try { 
221                  line = (SourceDataLine) AudioSystem.getLine(info); 
222                  line.open(format, bufSize); 
223              } catch (LineUnavailableException ex) { 
224                  shutDown("Unable to open the line: " + ex); 
225                  return; 
226              } 
227              // play back the captured audio data 
228   
229              int frameSizeInBytes = format.getFrameSize(); 
230              int bufferLengthInFrames = line.getBufferSize() / 8; 
231              int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes; 
232              byte[] data = new byte[bufferLengthInBytes]; 
233              int numBytesRead = 0; 
234   
235              // start the source data line 
236              line.start(); 
237              while (thread != null) { 
238                  try { 
239                      if ((numBytesRead = playbackInputStream.read(data)) == -1) { 
240                          break; 
241                      } 
242                      int numBytesRemaining = numBytesRead; 
243                      while (numBytesRemaining > 0) { 
244                          numBytesRemaining -= line.write(data, 0, numBytesRemaining); 
245                      } 
246                  } catch (Exception e) { 
247                      shutDown("Error during playback: " + e); 
248                      break; 
249                  } 
250              } 
251              // we reached the end of the stream.  let the data play out, then 
252              // stop and close the line. 
253              if (thread != null) { 
254                  line.drain(); 
255              } 
256              line.stop(); 
257              line.close(); 
258              line = null; 
259              shutDown(null); 
260          } 
261      } // End class Playback 
262   
263      /** 
264       * Reads data from the input channel and writes to the output stream 
265       */ 
266      class Capture implements Runnable { 
267          TargetDataLine targetDataLine; 
268          Thread thread; 
269   
270          public void start() { 
271              errStr = null; 
272              thread = new Thread(this); 
273              thread.setName("Capture"); 
274              thread.start(); 
275          } 
276   
277          public void stop() { 
278              thread = null; 
279          } 
280   
281          private void shutDown(String message) { 
282              if ((errStr = message) != null && thread != null) { 
283                  thread = null; 
284                  playB.setEnabled(true); 
285                  captB.setText("Record"); 
286                  System.err.println(errStr); 
287              } 
288          } 
289   
290          public void run() { 
291              audioInputStream = null; 
292   
293              // define the required attributes for our line, 
294              // and make sure a compatible line is supported. 
295   
296              AudioFormat format = formatControls.getFormat(); 
297              DataLine.Info info = new DataLine.Info(TargetDataLine.class, 
298                      format); 
299              if (!AudioSystem.isLineSupported(info)) { 
300                  shutDown("Line matching " + info + " not supported."); 
301                  return; 
302              } 
303   
304              // get and open the target data line for capture. 
305   
306              try { 
307                  targetDataLine = (TargetDataLine) AudioSystem.getLine(info); 
308                  targetDataLine.open(format, targetDataLine.getBufferSize()); 
309              } catch (LineUnavailableException ex) { 
310                  shutDown("Unable to open the line: " + ex); 
311                  return; 
312              } catch (SecurityException ex) { 
313                  shutDown(ex.toString()); 
314                  JavaSound.showInfoDialog(); 
315                  return; 
316              } catch (Exception ex) { 
317                  shutDown(ex.toString()); 
318                  return; 
319              } 
320   
321              // play back the captured audio data 
322              ByteArrayOutputStream out = new ByteArrayOutputStream(); 
323              int frameSizeInBytes = format.getFrameSize(); 
324              int bufferLengthInFrames = targetDataLine.getBufferSize() / 8; 
325              int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes; 
326              byte[] data = new byte[bufferLengthInBytes]; 
327              int numBytesRead; 
328              targetDataLine.start(); 
329              while (thread != null) { 
330                  if ((numBytesRead = targetDataLine.read(data, 0, bufferLengthInBytes)) == -1) { 
331                      break; 
332                  } 
333                  out.write(data, 0, numBytesRead); 
334              } 
335   
336              // we reached the end of the stream.  stop and close the line. 
337              targetDataLine.stop(); 
338              targetDataLine.close(); 
339              targetDataLine = null; 
340   
341              // stop and close the output stream 
342              try { 
343                  out.flush(); 
344                  out.close(); 
345              } catch (IOException ex) { 
346                  ex.printStackTrace(); 
347              } 
348   
349              // load bytes into the audio input stream for playback 
350   
351              getAudioInputStream(out, format, frameSizeInBytes); 
352              try { 
353                  audioInputStream.reset(); 
354              } catch (Exception ex) { 
355                  ex.printStackTrace(); 
356                  return; 
357              } 
358              createWaveForm(); 
359          } 
360   
361          private void getAudioInputStream(ByteArrayOutputStream out, 
362                                           AudioFormat format, int frameSizeInBytes) { 
363              byte audioBytes[] = out.toByteArray(); 
364              ByteArrayInputStream bais = new ByteArrayInputStream(audioBytes); 
365              audioInputStream = new AudioInputStream(bais, format, 
366                      audioBytes.length / frameSizeInBytes); 
367          } 
368      } // End class Capture 
369   
370      /** 
371       * Controls for the AudioFormat. 
372       */ 
373     public static  class FormatControls extends JPanel { 
374          Vector groups = new Vector(); 
375          JToggleButton linrB, ulawB, alawB, rate8B, rate11B, rate16B, rate22B, rate44B; 
376          JToggleButton size8B, size16B, signB, unsignB, litB, bigB, monoB, sterB; 
377   
378          public FormatControls() { 
379              setLayout(new GridLayout(0, 1)); 
380              CompoundBorder cb = new CompoundBorder(); 
381              setBorder(new CompoundBorder(cb, new EmptyBorder(8, 5, 5, 5))); 
382              JPanel p1 = new JPanel(); 
383              ButtonGroup encodingGroup = new ButtonGroup(); 
384              linrB = addToggleButton(p1, encodingGroup, "linear", true); 
385              groups.addElement(encodingGroup); 
386              ButtonGroup sampleRateGroup = new ButtonGroup(); 
387              rate8B = addToggleButton(p1, sampleRateGroup, "8000", false); 
388              rate11B = addToggleButton(p1, sampleRateGroup, "11025", false); 
389              rate16B = addToggleButton(p1, sampleRateGroup, "16000", false); 
390              rate22B = addToggleButton(p1, sampleRateGroup, "22050", false); 
391              rate44B = addToggleButton(p1, sampleRateGroup, "44100", true); 
392              groups.addElement(sampleRateGroup); 
393              JPanel p3 = new JPanel(); 
394              ButtonGroup sampleSizeInBitsGroup = new ButtonGroup(); 
395              size8B = addToggleButton(p3, sampleSizeInBitsGroup, "8", false); 
396              size16B = addToggleButton(p3, sampleSizeInBitsGroup, "16", true); 
397              add(p3); 
398              groups.addElement(sampleSizeInBitsGroup); 
399              JPanel p4 = new JPanel(); 
400              ButtonGroup signGroup = new ButtonGroup(); 
401              signB = addToggleButton(p4, signGroup, "signed", true); 
402              unsignB = addToggleButton(p4, signGroup, "unsigned", false); 
403              add(p4); 
404              groups.addElement(signGroup); 
405              JPanel p5 = new JPanel(); 
406              ButtonGroup endianGroup = new ButtonGroup(); 
407              litB = addToggleButton(p5, endianGroup, "little endian", false); 
408              bigB = addToggleButton(p5, endianGroup, "big endian", true); 
409              add(p5); 
410              groups.addElement(endianGroup); 
411              JPanel p6 = new JPanel(); 
412              ButtonGroup channelsGroup = new ButtonGroup(); 
413              monoB = addToggleButton(p6, channelsGroup, "mono", false); 
414              sterB = addToggleButton(p6, channelsGroup, "stereo", true); 
415              add(p6); 
416              groups.addElement(channelsGroup); 
417          } 
418   
419          private JToggleButton addToggleButton(JPanel p, ButtonGroup g, 
420                                                String name, boolean state) { 
421              JToggleButton b = new JToggleButton(name, state); 
422              p.add(b); 
423              g.add(b); 
424              return b; 
425          } 
426   
427          public AudioFormat getFormat() { 
428              Vector v = new Vector(groups.size()); 
429              for (int i = 0; i < groups.size(); i++) { 
430                  ButtonGroup g = (ButtonGroup) groups.get(i); 
431                  for (Enumeration e = g.getElements(); e.hasMoreElements();) { 
432                      AbstractButton b = (AbstractButton) e.nextElement(); 
433                      if (b.isSelected()) { 
434                          v.add(b.getText()); 
435                          break; 
436                      } 
437                  } 
438              } 
439              AudioFormat.Encoding encoding = AudioFormat.Encoding.ULAW; 
440              String encString = (String) v.get(0); 
441              float rate = Float.valueOf((String) v.get(1)).floatValue(); 
442              int sampleSize = Integer.valueOf((String) v.get(2)).intValue(); 
443              String signedString = (String) v.get(3); 
444              boolean bigEndian = ((String) v.get(4)).startsWith("big"); 
445              int channels = (v.get(5)).equals("mono") ? 1 : 2; 
446              if (encString.equals("linear")) { 
447                  if (signedString.equals("signed")) { 
448                      encoding = AudioFormat.Encoding.PCM_SIGNED; 
449                  } else { 
450                      encoding = AudioFormat.Encoding.PCM_UNSIGNED; 
451                  } 
452              } 
453              return new AudioFormat(encoding, rate, sampleSize, 
454                      channels, (sampleSize / 8) * channels, rate, bigEndian); 
455          } 
456      } // End class FormatControls 
457   
458      public static void main(String[] args) { 
459          CapturePlayBackPanel cpbp = new CapturePlayBackPanel(); 
460          gui.ClosableJFrame cf = new gui.ClosableJFrame(); 
461          Container c = cf.getContentPane(); 
462          c.add(cpbp); 
463          cf.setSize(400, 400); 
464          cf.show(); 
465      } 
466  }