/Users/lyon/j4p/src/sound/player/LiveSound.java
|
1 package sound.player;
2
3 import javax.sound.sampled.*;
4 import java.io.IOException;
5 import java.util.Iterator;
6 import java.util.LinkedList;
7 import java.util.List;
8
9 /**
10 * Created by IntelliJ IDEA.
11 * User: Douglas Lyon
12 * Date: Dec 13, 2004
13 * Time: 8:25:22 PM
14 * Copyright DocJava, Inc.
15 */
16 public class LiveSound {
17
18 /**
19 * Add a live sound listener. The listener will be notified
20 * of all changes in live audio parameters. If the listener
21 * is already listening, then do nothing.
22 *
23 * @param listener The LiveSoundListener to add.
24 */
25 public static void addLiveSoundListener(LiveSoundListener listener) {
26 if (!_liveSoundListeners.contains(listener)) {
27 _liveSoundListeners.add(listener);
28 }
29 }
30
31 /**
32 * Remove a live sound listener. If the listener is
33 * is not listening, then do nothing.
34 *
35 * @param listener The LiveSoundListener to remove.
36 */
37 public static void removeLibeSoundListener(LiveSoundListener listener) {
38 if (_liveSoundListeners.contains(listener)) {
39 _liveSoundListeners.remove(listener);
40 }
41 }
42
43 /**
44 * Return the current sampling rate in Hz, which is set
45 * by the setSampleRate() method. The default value of
46 * this parameter is 8000 Hz.
47 *
48 * @return The sample rate in Hz.
49 */
50 public static int getSampleRate() {
51 return (int) _sampleRate;
52 }
53
54 /**
55 * Return the number of bits per audio sample, which is
56 * set by the setBitsPerSample() method. The default
57 * value of this parameter is 16 bits.
58 *
59 * @return The sample size in bits.
60 */
61 public static int getBitsPerSample() {
62 return _bitsPerSample;
63 }
64
65 /**
66 * Return the number of audio channels, which is set by
67 * the setChannels() method. The default value of this
68 * parameter is 1 (for mono audio).
69 *
70 * @return The number of audio channels.
71 */
72 public static int getChannels() {
73 return _channels;
74 }
75
76 /**
77 * Return the size of the internal capture and playback
78 * audio buffers, in samples per channel. This parameter
79 * is set by the setBufferSize() method. The default
80 * value of this parameter is 4096.
81 *
82 * @return The internal buffer size in samples per
83 * channel.
84 */
85 public static int getBufferSize() {
86 return _bufferSize;
87 }
88
89 /**
90 * Return true if audio capture is currently active.
91 * Otherwise return false.
92 *
93 * @return True If audio capture is currently active.
94 * Otherwise return false.
95 */
96 public static boolean isCaptureActive() {
97 return _captureIsActive;
98 }
99
100 /**
101 * Return true if audio playback is currently active.
102 * Otherwise return false.
103 *
104 * @return True If audio playback is currently active.
105 * Otherwise return false.
106 */
107 public static boolean isPlaybackActive() {
108 return _playbackIsActive;
109 }
110
111 /**
112 * Set the sample rate to use for audio capture and playback
113 * and notify an registered listeners of the change.
114 * Allowable values for this parameter are 8000, 11025,
115 * 22050, 44100, and 48000 Hz. If this method is not invoked,
116 * then the default value of 8000 Hz is used.
117 *
118 * @param sampleRate Sample rate in Hz.
119 * @throws IOException If the specified sample rate is
120 * not supported by the audio hardware or by Java.
121 */
122 public static void setSampleRate(int sampleRate)
123 throws IOException {
124 _sampleRate = (float) sampleRate;
125 if (_debug) {
126 System.out.println("LiveSound: setSampleRate() invoked " +
127 "with sample rate = " + sampleRate);
128 }
129 if ((_captureIsActive) && (_playbackIsActive)) {
130 // Restart capture/playback with new sample rate.
131 if (_debug) {
132 System.out.println("LiveSound: setSampleRate(): capture " +
133 "and playback are active..");
134 }
135 _stopCapture();
136 _stopPlayback();
137 _startCapture();
138 _startPlayback();
139 } else if (_captureIsActive) {
140 // Restart capture with new sample rate.
141 if (_debug) {
142 System.out.println("LiveSound: setSampleRate(): capture " +
143 "is active..");
144 }
145 _stopCapture();
146 _startCapture();
147 } else if (_playbackIsActive) {
148 // Restart playback with new sample rate.
149 if (_debug) {
150 System.out.println("LiveSound: setSampleRate(): " +
151 "playback is active..");
152 }
153 _stopPlayback();
154 _startPlayback();
155 }
156 // Notify listeners of the change.
157 _notifyLiveSoundListeners(LiveSoundEvent.SAMPLE_RATE);
158 if (_debug) {
159 System.out.println("LiveSound: setSampleRate() " +
160 "returning now.");
161 }
162 }
163
164 /**
165 * Set the number of bits per sample to use for audio capture
166 * and playback and notify an registered listeners of the change.
167 * Allowable values include 8 and 16 bits. If
168 * this method is not invoked, then the default value of 16
169 * bits is used.
170 *
171 * @param bitsPerSample The number of bits per sample.
172 * @throws IOException If the specified bits per sample is
173 * not supported by the audio hardware or by Java.
174 */
175 public static void setBitsPerSample(int bitsPerSample)
176 throws IOException {
177 _bitsPerSample = bitsPerSample;
178 if (_debug) {
179 System.out.println("LiveSound: setBitsPerSample() invoked " +
180 "with bitsPerSample = " + bitsPerSample);
181 }
182 if ((_captureIsActive) && (_playbackIsActive)) {
183 // Restart capture/playback with new bitsPerSample.
184 if (_debug) {
185 System.out.println("LiveSound: setBitsPerSample(): " +
186 "capture and playback are active..");
187 }
188 _stopCapture();
189 _stopPlayback();
190 _startCapture();
191 _startPlayback();
192 } else if (_captureIsActive) {
193 // Restart capture with new bitsPerSample.
194 if (_debug) {
195 System.out.println("LiveSound: setBitsPerSample(): " +
196 "capture is active..");
197 }
198 _stopCapture();
199 _startCapture();
200 } else if (_playbackIsActive) {
201 // Restart playback with new bitsPerSample.
202 if (_debug) {
203 System.out.println("LiveSound: setBitsPerSample(): " +
204 "playback is active..");
205 }
206 _stopPlayback();
207 _startPlayback();
208 }
209 // Notify listeners of the change.
210 _notifyLiveSoundListeners(LiveSoundEvent.BITS_PER_SAMPLE);
211 if (_debug) {
212 System.out.println("LiveSound: setBitsPerSample() " +
213 "returning now.");
214 }
215 }
216
217 /**
218 * Set the number of audio channels to use for capture and
219 * playback and notify an registered listeners of the change.
220 * Allowable values are 1 (for mono) and 2 (for
221 * stereo). If this method is not invoked, the default
222 * value of 1 audio channel is used. Note that this method
223 * sets the size of the first dimension of the
224 * 2-dimensional array used by the putSamples() and
225 * getSamples() methods.
226 *
227 * @param channels The number audio channels.
228 * @throws IOException If the specified number of channels is
229 * not supported by the audio hardware or by Java.
230 */
231 public static void setChannels(int channels)
232 throws IOException {
233 _channels = channels;
234 if (_debug) {
235 System.out.println("LiveSound: setChannels() invoked " +
236 "with channels = " + channels);
237 }
238 if ((_captureIsActive) && (_playbackIsActive)) {
239 // Restart capture/playback with new number of channels.
240 if (_debug) {
241 System.out.println("LiveSound: setChannels(): " +
242 "capture and playback are active..");
243 }
244 _stopCapture();
245 _stopPlayback();
246 _startCapture();
247 _startPlayback();
248 } else if (_captureIsActive) {
249 // Restart capture with new number of channels.
250 if (_debug) {
251 System.out.println("LiveSound: setChannels(): capture " +
252 "is active..");
253 }
254 _stopCapture();
255 _startCapture();
256 } else if (_playbackIsActive) {
257 // Restart playback with new number of channels.
258 if (_debug) {
259 System.out.println("LiveSound: setChannels(): playback " +
260 "is active..");
261 }
262 _stopPlayback();
263 _startPlayback();
264 }
265 // Notify listeners of the change.
266 _notifyLiveSoundListeners(LiveSoundEvent.CHANNELS);
267 if (_debug) {
268 System.out.println("LiveSound: setSampleRate() " +
269 "returning now.");
270 }
271 }
272
273 /**
274 * Set the size of the internal capture and playback
275 * audio buffers, in samples per channel and notify an
276 * registered listeners of the change. If this method
277 * is not invoked, the default value of 4096 is used.
278 *
279 * @param bufferSize The size of the internal capture and
280 * playback audio buffers, in samples per channel.
281 * @throws IOException If the specified number of channels is
282 * not supported by the audio hardware or by Java.
283 */
284 public static void setBufferSize(int bufferSize)
285 throws IOException {
286 _bufferSize = bufferSize;
287 if (_debug) {
288 System.out.println("LiveSound: setBufferSize() invoked " +
289 "with bufferSize = " + bufferSize);
290 }
291 if ((_captureIsActive) && (_playbackIsActive)) {
292 // Restart capture/playback with new bufferSize.
293 if (_debug) {
294 System.out.println("LiveSound: setBufferSize(): " +
295 "capture and playback are active..");
296 }
297 _stopCapture();
298 _stopPlayback();
299 _startCapture();
300 _startPlayback();
301 } else if (_captureIsActive) {
302 // Restart capture with new bufferSize.
303 if (_debug) {
304 System.out.println("LiveSound: setBufferSize(): capture " +
305 "is active..");
306 }
307 _stopCapture();
308 _startCapture();
309 } else if (_playbackIsActive) {
310 // Restart playback with new bufferSize.
311 if (_debug) {
312 System.out.println("LiveSound: setBufferSize(): " +
313 "playback is active..");
314 }
315 _stopPlayback();
316 _startPlayback();
317 }
318 // Notify listeners of the change.
319 _notifyLiveSoundListeners(LiveSoundEvent.BUFFER_SIZE);
320 if (_debug) {
321 System.out.println("LiveSound: setBufferSize() " +
322 "returning now.");
323 }
324 }
325
326 /**
327 * Set the array length (in samples per channel) to use
328 * for capturing and playing samples via the putSamples()
329 * and getSamples() methods. This method sets the size
330 * of the 2nd dimension of the 2-dimensional array
331 * used by the putSamples() and getSamples() methods. If
332 * this method is not invoked, the default value of 128 is
333 * used.
334 * <p/>
335 * This method should only be called while audio capture and
336 * playback are inactive. Otherwise an exception will occur.
337 *
338 * @param transferSize The size of the 2nd dimension of
339 * the 2-dimensional array used by the putSamples() and
340 * getSamples() methods
341 * @throws IllegalStateException If this method is called
342 * while audio capture or playback are active.
343 */
344 public static void setTransferSize(int transferSize)
345 throws IllegalStateException {
346 if (_debug) {
347 System.out.println("LiveSound: " +
348 "setTransferSize(transferSize) " +
349 " invoked with transferSize = " +
350 transferSize);
351 }
352 if ((_captureIsActive) || (_playbackIsActive)) {
353 throw new IllegalStateException("LiveSound: " +
354 "setTransferSize() was called while audio capture " +
355 "or playback was active.");
356
357 } else {
358 _transferSize = transferSize;
359 }
360 }
361
362 /**
363 * Get the array length (in samples per channel) to use
364 * for capturing and playing samples via the putSamples()
365 * and getSamples() methods. This method gets the size
366 * of the 2nd dimension of the 2-dimensional array
367 * used by the putSamples() and getSamples() methods. This
368 * method returns the value that was set by the
369 * setTransferSize(). If setTransferSize() was not invoked,
370 * the default value of 128 is returns.
371 * <p/>
372 * This method should only be called while audio capture and
373 * playback are inactive. Otherwise an exception will occur.
374 *
375 * @return The size of the 2nd dimension of the 2-dimensional
376 * array used by the putSamples() and getSamples() methods.
377 */
378 public static int getTransferSize() {
379 return _transferSize;
380 }
381
382 /**
383 * Stop audio capture. If audio capture is already inactive,
384 * then do nothing. This method should generally not be used,
385 * but it may be needed to turn of audio capture for the
386 * case where an ill-behaved application exits without calling
387 * stopCapture(). The preferred way of stopping audio capture
388 * is by calling the stopCapture() method.
389 */
390 public static void resetCapture() {
391 if (_targetLine != null) {
392
393 if (_targetLine.isOpen() == true) {
394 _targetLine.stop();
395 _targetLine.close();
396 _targetLine = null;
397 }
398 }
399 _captureIsActive = false;
400 }
401
402 /**
403 * Stop audio playback. If audio playback is already inactive,
404 * then do nothing. This method should generally not be used,
405 * but it may be needed to turn of audio playback for the
406 * case where an ill-behaved application exits without calling
407 * stopPlayback(). The preferred way of stopping audio playback
408 * is by calling the stopPlayback() method.
409 */
410 public static void resetPlayback() {
411 _stopPlayback();
412 _playbackIsActive = false;
413 }
414
415 /**
416 * Start audio capture. The specified object will be
417 * given an exclusive lock on the audio capture resources
418 * until the stopCapture() method is called with the
419 * same object reference. After this method returns,
420 * the getSamples() method may be repeatedly invoked
421 * (using the object reference as a parameter) to
422 * capture audio.
423 * <p/>
424 * If audio capture is already active, then an
425 * exception will occur.
426 *
427 * @param consumer The object to be given exclusive access
428 * to the captured audio resources.
429 * @throws IllegalStateException If this method is called
430 * while audio capture is already active.
431 */
432 public static void startCapture(Object consumer)
433 throws IOException, IllegalStateException {
434 // FXIME: consider allowing several object to
435 // share the captured audio resources.
436 if (_soundConsumers.size() > 1) {
437 throw new IOException("Object: " + consumer.toString() +
438 " is not allowed to start audio capture because " +
439 " another object currently has access to the audio" +
440 " capture resources.");
441 }
442 if (!_soundConsumers.contains(consumer)) {
443 _soundConsumers.add(consumer);
444 } else {
445 throw new IOException("Object: " + consumer.toString() +
446 "attempted to call LiveSound.startCapture() while " +
447 "audio capture was active. Only one object may " +
448 "access the audio capture resources at a time.");
449 }
450 if (_debug) {
451 System.out.println("LiveSound: startCapture(): invoked");
452 }
453 // This is a workaround for a javasound bug. In javasound,
454 // when doing simultaneous capture and playback, the
455 // capture process must be started first. So, if
456 // there is already a playback process running then
457 // stop it before starting capture.
458 if (isPlaybackActive()) {
459 _stopPlayback();
460 _startCapture();
461 _startPlayback();
462 } else {
463 _startCapture();
464 }
465 _captureIsActive = true;
466 }
467
468 /**
469 * Stop audio capture. If the specified object has
470 * the lock on audio capture when this method is
471 * invoked, then stop audio capture. Otherwise
472 * an exception will occur.
473 *
474 * @param consumer The object that held on exclusive
475 * lock on the captured audio resources when this
476 * method was invoked.
477 * @throws IllegalStateException If the specified
478 * object did not hold an exclusive lock on the
479 * captured audio resources when this method was invoked.
480 */
481 public static void stopCapture(Object consumer)
482 throws IOException, IllegalStateException {
483 if (_debug) {
484 System.out.println("LiveSound: stopCapture(): invoked");
485 }
486 if (_soundConsumers.contains(consumer)) {
487 _soundConsumers.remove(consumer);
488 } else {
489 throw new IOException("Object: " + consumer.toString() +
490 "attempted to call LiveSound.stopCapture(), but " +
491 "never called LiveSound.startCapture().");
492 }
493 // Free up audio system resources.
494 _stopCapture();
495 _captureIsActive = false;
496 }
497
498 /**
499 * Start audio playback. The specified object will be
500 * given an exclusive lock on the audio playback resources
501 * until the stopPlayback() method is called with the
502 * same object reference. After this method returns,
503 * the putSamples() method may be repeatedly invoked
504 * (using the object reference as a parameter) to
505 * playback audio.
506 * <p/>
507 * If audio playback is already active, then an
508 * exception will occur.
509 *
510 * @param producer The object to be given exclusive access
511 * to the playback playback resources.
512 * @throws IllegalStateException If this method is called
513 * while audio playback is already active.
514 */
515 public static void startPlayback(Object producer)
516 throws IOException, IllegalStateException {
517 if (_soundProducers.size() > 1) {
518 throw new IOException("Object: " + producer.toString() +
519 " is not allowed to start audio playback because " +
520 " another object currently has access to the audio" +
521 " playback resources.");
522 }
523 if (!_soundProducers.contains(producer)) {
524 _soundProducers.add(producer);
525 } else {
526 throw new IOException("Object: " + producer.toString() +
527 "attempted to call LiveSound.startPlayback() while " +
528 "audio playback was active. Only one object may " +
529 "access the audio playback resources at a time.");
530 }
531 if (_debug) {
532 System.out.println("LiveSound: startPlayback() invoked");
533 }
534 _startPlayback();
535 _playbackIsActive = true;
536 }
537
538 /**
539 * Stop audio playback. If the specified object has
540 * the lock on audio playback when this method is
541 * invoked, then stop audio playback. Otherwise
542 * an exception will occur.
543 *
544 * @param producer The object that held on exclusive
545 * lock on the playback audio resources when this
546 * method was invoked.
547 * @throws IllegalStateException If the specified
548 * object did not hold an exclusive lock on the
549 * playback audio resources when this method was invoked.
550 */
551 public static void stopPlayback(Object producer)
552 throws IOException, IllegalStateException {
553 if (_soundProducers.contains(producer)) {
554 _soundProducers.remove(producer);
555 } else {
556 throw new IOException("Object: " + producer.toString() +
557 "attempted to call LiveSound.stopPlayback(), but " +
558 "never called LiveSound.startPlayback().");
559 }
560 if (_debug) {
561 System.out.println("LiveSound: stopPlayback() invoked");
562 }
563 _stopPlayback();
564 _playbackIsActive = false;
565 }
566
567 /**
568 * Return an array of captured audio samples. This method
569 * should be repeatedly called to obtain audio data.
570 * The returned audio samples will have values in the range
571 * [-1, 1], regardless of the audio bit resolution (bits per
572 * sample). This method should be called often enough to
573 * prevent overflow of the internal audio buffer. If
574 * overflow occurs, some audio data will be lost but no
575 * exception or other error condition will occur. If
576 * the audio data is not yet available, then this method
577 * will block until the data is available.
578 * <p/>
579 * The first index of the returned array
580 * represents the channel number (0 for first channel, 1 for
581 * second channel). The number of channels is set by the
582 * setChannels() method. The second index represents the
583 * sample index within a channel. For example,
584 * <i>returned array</i>[n][m] contains the (m+1)th sample
585 * of the (n+1)th channel. For each channel, n, the length of
586 * <i>returned array</i>[n] is equal to the value returned by
587 * the getTransferSize() method.
588 * The size of the 2nd dimension of the returned array
589 * is set by the setTransferSize() method.
590 * <p/>
591 * Note that only the object with the exclusive lock on
592 * the captured audio resources is allowed to invoked this
593 * method. An exception will occur if the specified object
594 * does not have the lock on the captured audio resources.
595 *
596 * @param consumer The object that has an exclusive lock on
597 * the captured audio resources.
598 * @return Two dimensional array of captured audio samples.
599 * @throws IOException If there is a problem capturing
600 * audio.
601 * @throws IllegalStateException If audio capture is
602 * currently inactive, or if the specified object does
603 * not hold the lock on the captured audio resources.
604 */
605 public static double[][] getSamples(Object consumer)
606 throws IOException, IllegalStateException {
607 if (!_soundConsumers.contains(consumer)) {
608 throw new IOException("Object: " + consumer.toString() +
609 "attempted to call LiveSound.getSamples(), but " +
610 "this object never called startCapture() and does " +
611 "not have permission to access the audio capture " +
612 "resource.");
613 }
614
615 if (_debug) {
616 System.out.println("LiveSound: getSamples(): invoked");
617 //System.out.println("LiveSound: getSamples(): " +
618 // "_transferSize = " + _transferSize);
619 }
620 int numBytesRead;
621
622 // Real-time capture.
623 numBytesRead = _targetLine.read(_data, 0,
624 _transferSize * _frameSizeInBytes);
625
626
627 if (numBytesRead == _data.length) {
628 // Convert byte array to double array.
629 _audioInDoubleArray =
630 _byteArrayToDoubleArray(_data,
631 _bytesPerSample,
632 _channels);
633 return _audioInDoubleArray;
634 } else if (numBytesRead != _data.length) {
635 // Read fewer samples than productionRate many samples.
636 // NOTE: There appears to be a java sound bug that
637 // causes AudioInputStream.read(array) to sometimes
638 // return fewer bytes than requested, even though
639 // the end of the file has not yet been reached.
640 _audioInDoubleArray =
641 _byteArrayToDoubleArray(_data,
642 _bytesPerSample,
643 _channels);
644 return _audioInDoubleArray;
645 } else if (numBytesRead == -1) {
646 // Ran out of samples to play. This generally means
647 // that the end of the sound file has been reached.
648 return null;
649 }
650 return null;
651
652 }
653
654 /**
655 * Play an array of audio samples. There will be a
656 * delay before the audio data is actually heard, since the
657 * audio data in <i>samplesArray</i> is queued to an
658 * internal audio buffer. The size of the internal buffer
659 * is set by the setTransferSize() method. A lower bound
660 * on the latency is given by (<i>bufferSize</i> /
661 * <i>sampleRate</i>) seconds. This method should be invoked often
662 * enough to prevent underflow of the internal audio buffer.
663 * Underflow is undesirable since it will cause audible gaps
664 * in audio playback, but no exception or error condition will
665 * occur. If the caller attempts to write more data than can
666 * be written, this method blocks until the data can be
667 * written to the internal audio buffer.
668 * <p/>
669 * The samples should be in the range (-1, 1). Samples that are
670 * outside this range will be hard-clipped so that they fall
671 * within this range.
672 * <p/>
673 * The first index of the specified array
674 * represents the channel number (0 for first channel, 1 for
675 * second channel, etc.). The number of channels is set by the
676 * setChannels() method. The second index represents the
677 * sample index within a channel. For example,
678 * putSamplesArray[n][m] contains the (m+1)th sample
679 * of the (n+1)th channel. samplesArray should be a
680 * rectangular array such that samplesArray.length() gives
681 * the number of channels and samplesArray[n].length() is
682 * equal to <i>samplesArray</i>, for all channels n. This
683 * is not actually checked, however.
684 * <p/>
685 * Note that only the object with the exclusive lock on
686 * the playback audio resources is allowed to invoked this
687 * method. An exception will occur if the specified object
688 * does not have the lock on the playback audio resources.
689 *
690 * @param producer The object that has an exclusive lock on
691 * the playback audio resources.
692 * @param samplesArray A two dimensional array containing
693 * the samples to play or write to a file.
694 * @throws IOException If there is a problem playing audio.
695 * @throws IllegalStateException If audio playback is currently
696 * inactive. That is, If startPlayback() has not yet been called
697 * or if stopPlayback() has already been called.
698 */
699 public static void putSamples(Object producer,
700 double[][] samplesArray)
701 throws IOException, IllegalStateException {
702 if (!_soundProducers.contains(producer)) {
703 throw new IOException("Object: " + producer.toString() +
704 "attempted to call LiveSound.putSamples(), but " +
705 "this object never called startPlayback() and does " +
706 "not have permission to access the audio playback " +
707 "resource.");
708 }
709 if (_debug) {
710 System.out.println("LiveSound: putSamples(): invoked");
711 }
712 // Convert array of double valued samples into
713 // the proper byte array format.
714 _data = _doubleArrayToByteArray(samplesArray,
715 _bytesPerSample,
716 _channels);
717
718 // Note: _data is a byte array containing data to
719 // be written to the output device.
720 // Note: consumptionRate is amount of data to write, in bytes.
721 // Now write the array to output device.
722 _sourceLine.write(_data, 0, _transferSize * _frameSizeInBytes);
723 }
724
725
726 ///////////////////////////////////////////////////////////////////
727 //// private methods ////
728
729 /**
730 * Start audio capture.
731 */
732 private static void _startCapture() throws IOException {
733
734 int frameSizeInBits = _bitsPerSample;
735 double frameRate = _sampleRate;
736 boolean signed = true;
737 boolean bigEndian = true;
738 AudioFormat format = new AudioFormat(_sampleRate,
739 _bitsPerSample,
740 _channels, signed, bigEndian);
741
742 _frameSizeInBytes = format.getFrameSize();
743 DataLine.Info targetInfo = new DataLine.Info(TargetDataLine.class,
744 format, AudioSystem.NOT_SPECIFIED);
745 try {
746 _targetLine = (TargetDataLine) AudioSystem.getLine(targetInfo);
747 // Note: 2nd parameter is the buffer size (in bytes).
748 // Larger values increase latency but may be required if
749 // garbage collection, etc. is an issue.
750 _targetLine.open(format, _bufferSize * _frameSizeInBytes);
751 } catch (LineUnavailableException ex) {
752 throw new IOException("Unable to open the line for " +
753 "real-time audio capture: " + ex);
754 }
755 int targetBufferLengthInBytes = _transferSize *
756 _frameSizeInBytes;
757 // Array of audio samples in byte format.
758 _data = new byte[_transferSize * _frameSizeInBytes];
759 _bytesPerSample = _bitsPerSample / 8;
760 // Start the target data line
761 _targetLine.start();
762 }
763
764 /**
765 * Start audio playback.
766 */
767 private static void _startPlayback() throws IOException {
768 boolean signed = true;
769 boolean bigEndian = true;
770
771 AudioFormat format = new AudioFormat((float) _sampleRate,
772 _bitsPerSample,
773 _channels, signed, bigEndian);
774
775 _frameSizeInBytes = format.getFrameSize();
776 DataLine.Info sourceInfo = new DataLine.Info(SourceDataLine.class,
777 format,
778 AudioSystem.NOT_SPECIFIED);
779 // get and open the source data line for playback.
780 try {
781 // Source DataLine is really a target for
782 // audio data, not a source.
783 _sourceLine = (SourceDataLine) AudioSystem.getLine(sourceInfo);
784 // Open line and suggest a buffer size (in bytes) to use or
785 // the internal audio buffer.
786 _sourceLine.open(format, _bufferSize * _frameSizeInBytes);
787 } catch (LineUnavailableException ex) {
788 throw new IOException("Unable to open the line for " +
789 "real-time audio playback: " + ex);
790 }
791 // Array of audio samples in byte format.
792 _data = new byte[_transferSize * _frameSizeInBytes * _channels];
793 _bytesPerSample = _bitsPerSample / 8;
794 // Start the source data line
795 _sourceLine.start();
796 }
797
798 /**
799 * Stop audio playback.
800 */
801 private static void _stopPlayback() {
802 if (_sourceLine != null) {
803 _sourceLine.drain();
804 _sourceLine.stop();
805 _sourceLine.close();
806 }
807 _sourceLine = null;
808 }
809
810 /**
811 * Stop audio capture.
812 */
813 private static void _stopCapture() {
814 if (_targetLine != null) {
815 if (_targetLine.isOpen() == true) {
816 _targetLine.stop();
817 _targetLine.close();
818 _targetLine = null;
819 }
820 }
821 }
822
823 /* Convert a byte array of audio samples in linear signed pcm big endian
824 * format into a double array of audio samples (-1, 1) range.
825 * @param byteArray The linear signed pcm big endian byte array
826 * formatted array representation of audio data.
827 * @param bytesPerSample Number of bytes per sample. Supported
828 * bytes per sample by this method are 8, 16, 24, 32.
829 * @param channels Number of audio channels. 1 for mono, 2 for
830 * stereo.
831 * @return Two dimensional array holding audio samples.
832 * For each channel, m, doubleArray[m] is a single dimensional
833 * array containing samples for channel m.
834 */
835 private static double[][] _byteArrayToDoubleArray(byte[] byteArray,
836 int bytesPerSample,
837 int channels) {
838 int lengthInSamples = byteArray.length / (bytesPerSample * channels);
839 // Check if we need to reallocate.
840 if ((channels != _doubleArray.length) ||
841 (lengthInSamples != _doubleArray[0].length)) {
842 // Reallocate
843 _doubleArray = new double[channels][lengthInSamples];
844 }
845 //double maxSampleReciprocal = 1/(Math.pow(2, 8 * bytesPerSample - 1));
846 // Could use above line, but hopefully, code below will
847 // be faster.
848 double maxSampleReciprocal;
849 if (bytesPerSample == 2) {
850 // 1 / 32768
851 maxSampleReciprocal = 3.0517578125e-5;
852 } else if (bytesPerSample == 1) { // 1 / 128
853 maxSampleReciprocal = 7.8125e-3;
854 } else if (bytesPerSample == 3) {
855 // 1 / 8388608
856 maxSampleReciprocal = 1.1920928955e07;
857 } else if (bytesPerSample == 4) {
858 // 1 / 147483648e9
859 maxSampleReciprocal = 4.655661287308e-10;
860 } else {
861 // Should not happen.
862 maxSampleReciprocal = 0;
863 }
864
865 // Check if we need to reallocate.
866 // Note: This test is really not needed since bytesPerSample
867 // is set in the constructor. It should never change.
868 if (bytesPerSample != _b.length) {
869 _b = new byte[bytesPerSample];
870 }
871
872 for (int currSamp = 0; currSamp < lengthInSamples; currSamp++) {
873
874 // For each channel,
875 for (int currChannel = 0; currChannel < channels; currChannel++) {
876 for (int i = 0; i < bytesPerSample; i += 1) {
877 // Assume we are dealing with big endian.
878 _b[i] = byteArray[currSamp * bytesPerSample * channels +
879 bytesPerSample * currChannel + i];
880 }
881 int result = (_b[0] >> 7);
882 for (int i = 0; i < bytesPerSample; i += 1)
883 result = (result << 8) + (_b[i] & 0xff);
884 _doubleArray[currChannel][currSamp] =
885 ((double) result * maxSampleReciprocal);
886 }
887 }
888 return _doubleArray;
889 }
890
891 /* Convert a double array of audio samples into a byte array of
892 * audio samples in linear signed pcm big endian format. The
893 * samples contained in <i>doubleArray</i> should be in the
894 * range (-1, 1). Samples outside this range will be hard clipped
895 * to the range (-1, 1).
896 * @param doubleArray Two dimensional array holding audio samples.
897 * For each channel, m, doubleArray[m] is a single dimensional
898 * array containing samples for channel m.
899 * @param bytesPerSample Number of bytes per sample. Supported
900 * bytes per sample by this method are 8, 16, 24, 32.
901 * @param channels Number of audio channels.
902 * @return The linear signed pcm big endian byte array formatted
903 * array representation of <i>doubleArray</i>. The length of
904 * the returned array is (doubleArray.length*bytesPerSample*channels).
905 */
906 private static byte[] _doubleArrayToByteArray(double[][] doubleArray,
907 int bytesPerSample, int channels) {
908 // All channels had better have the same number
909 // of samples! This is not checked!
910 int lengthInSamples = doubleArray[0].length;
911 //double maxSample = Math.pow(2, 8 * bytesPerSample - 1);
912 // Could use above line, but hopefully, code below will
913 // be faster.
914 double maxSample;
915 double maxDoubleValuedSample;
916 if (bytesPerSample == 2) {
917 maxSample = 32768;
918 } else if (bytesPerSample == 1) {
919 maxSample = 128;
920 } else if (bytesPerSample == 3) {
921 maxSample = 8388608;
922 } else if (bytesPerSample == 4) {
923 maxSample = 147483648e9;
924 } else {
925 // Should not happen.
926 maxSample = 0;
927 }
928 maxDoubleValuedSample = (maxSample - 2) / maxSample;
929 byte[] byteArray =
930 new byte[lengthInSamples * bytesPerSample * channels];
931 byte[] b = new byte[bytesPerSample];
932 for (int currSamp = 0; currSamp < lengthInSamples; currSamp++) {
933
934 int l;
935 // For each channel,
936 for (int currChannel = 0; currChannel < channels; currChannel++) {
937 // Perform clipping, if necessary.
938 if (doubleArray[currChannel][currSamp] >=
939 maxDoubleValuedSample) {
940 l = (int) maxSample - 2;
941 } else if (doubleArray[currChannel][currSamp] <=
942 -maxDoubleValuedSample) {
943 l = (int) (-maxSample) + 2;
944 } else {
945 // signed integer representation of current sample of the
946 // current channel.
947 l =
948 (int) (doubleArray[currChannel][currSamp] * maxSample);
949 }
950 // Create byte representation of current sample.
951 for (int i = 0; i < bytesPerSample; i += 1, l >>= 8)
952 b[bytesPerSample - i - 1] = (byte) l;
953 // Copy the byte representation of current sample to
954 // the linear signed pcm big endian formatted byte array.
955 for (int i = 0; i < bytesPerSample; i += 1) {
956 byteArray[currSamp * bytesPerSample * channels +
957 bytesPerSample * currChannel + i] = b[i];
958 }
959 }
960 }
961 return byteArray;
962 }
963
964 /**
965 * Notify the live sound listeners about a change in an audio
966 * parameter.
967 *
968 * @param parameter The audio parameter of LiveSound that
969 * has changed. The value of parameter should be one of
970 * LiveSoundEvent.SAMPLE_RATE, LiveSoundEvent.CHANNELS,
971 * LiveSoundEvent.BUFFER_SIZE, or
972 * LiveSoundEvent.BITS_PER_SAMPLE.
973 */
974 private static void _notifyLiveSoundListeners(int parameter) {
975 if (_liveSoundListeners.size() > 0) {
976 LiveSoundEvent event = new LiveSoundEvent(parameter);
977 Iterator listeners = _liveSoundListeners.iterator();
978 while (listeners.hasNext()) {
979 ((LiveSoundListener) listeners.next()).liveSoundChanged(event);
980 }
981 }
982 }
983
984 ///////////////////////////////////////////////////////////////////
985 //// private variables ////
986
987 // Array of audio samples in double format.
988 private static double[][] _audioInDoubleArray;
989 private static byte[] _b = new byte[1];
990 private static int _bitsPerSample = 16;
991 private static int _bufferSize = 4096;
992 private static int _bytesPerSample;
993 // true is audio capture is currently active
994 private static boolean _captureIsActive = false;
995 private static int _channels;
996 // Array of audio samples in byte format.
997 private static byte[] _data;
998 private static double[][] _doubleArray = new double[1][1];
999 private static int _frameSizeInBytes;
1000 // true is audio playback is currently active
1001 private static boolean _playbackIsActive = false;
1002 private static float _sampleRate;
1003 private static SourceDataLine _sourceLine;
1004 private static TargetDataLine _targetLine;
1005 // the number of audio samples to transfer per channel
1006 // when putSamples() or getSamples() is invoked.
1007 private static int _transferSize = 128;
1008 private static List _liveSoundListeners = new LinkedList();
1009 private static List
1010
1011 _soundConsumers = new LinkedList();
1012 private static List _soundProducers = new LinkedList();
1013 // for debugging;
1014 //private static boolean _debug = true;
1015 private static boolean _debug = false;
1016 }
1017