/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 }