001/*
002 Implementation of LiveSoundInterface actor that in dependent on javax.
003
004 Copyright (c) 2011-2018 The Regents of the University of California.
005 All rights reserved.
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
016 SUCH DAMAGE.
017
018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
023 ENHANCEMENTS, OR MODIFICATIONS.
024
025 PT_COPYRIGHT_VERSION_2
026 COPYRIGHTENDKEY
027 */
028
029package ptolemy.media.javasound;
030
031import java.io.IOException;
032
033import javax.sound.sampled.AudioFormat;
034import javax.sound.sampled.AudioFormat.Encoding;
035import javax.sound.sampled.AudioSystem;
036import javax.sound.sampled.DataLine;
037import javax.sound.sampled.LineUnavailableException;
038import javax.sound.sampled.SourceDataLine;
039import javax.sound.sampled.TargetDataLine;
040
041///////////////////////////////////////////////////////////////////
042//// LiveSoundJavaSE
043
044/**
045 Implementation of LiveSoundInterface that in dependent on javax.
046 This actor sequentially outputs audio samples that are captured
047 from the audio input port of the computer. The audio input port
048 typically corresponds to either the microphone input, line-in,
049 or cd audio from the cdrom or dvd drive. It is not possible to
050 select the desired input port under Java. This must be done from
051 the operating system. This actor should be fired often enough to
052 prevent overflow of the internal audio capture buffer.
053 Overflow should be avoided, since it will result in loss of
054 data. Each captured audio sample is converted to a double that
055 may range from -1.0 to 1.0. Thus, the output type of this actor
056 is DoubleToken.
057
058
059 @author Brian K. Vogel and Neil E. Turner and Steve Neuendorffer, Edward A. Lee, Contributor: Dennis Geurts, Ishwinder Singh
060 @version $Id$
061 @since Ptolemy II 10.0
062 @Pt.ProposedRating Red (ishwinde)
063 @Pt.AcceptedRating Red (ishwinde)
064 */
065public class LiveSoundJavaSE extends LiveSoundCommon
066        implements LiveSoundInterface {
067
068    /** Flush queued data from the capture buffer.  The flushed data is
069     *  discarded.  It is only legal to flush the capture buffer after
070     *  startCapture() is called.  Flushing an active audio buffer is likely to
071     *  cause a discontinuity in the data, resulting in a perceptible click.
072     *  <p>
073     *  Note that only the object with the exclusive lock on the capture audio
074     *  resources is allowed to invoke this method. An exception will occur if
075     *  the specified object does not have the lock on the playback audio
076     *  resources.
077     *
078     *  @param consumer The object that has an exclusive lock on
079     *   the capture audio resources.
080     *
081     *  @exception IllegalStateException If audio capture is currently
082     *  inactive. That is, if startCapture() has not yet been called
083     *  or if stopCapture() has already been called.
084     *
085     *  @exception IOException If the calling program does not have permission
086     *  to access the audio capture resources.
087     */
088    @Override
089    public void flushCaptureBuffer(Object consumer)
090            throws IOException, IllegalStateException {
091        if (!isCaptureActive()) {
092            throw new IllegalStateException("Object: " + consumer.toString()
093                    + " attempted to call LiveSound.flushCaptureBuffer(), but "
094                    + "capture is inactive.  Try to startCapture().");
095        }
096
097        if (!_soundConsumers.contains(consumer)) {
098            throw new IOException("Object: " + consumer.toString()
099                    + " attempted to call LiveSound.flushCaptureBuffer(), but "
100                    + "this object does not have permission to access the "
101                    + "audio capture resource.");
102        }
103        _flushCaptureBuffer();
104    }
105
106    /** Flush queued data from the playback buffer.  The flushed data is
107     *  discarded.  It is only legal to flush the playback buffer after
108     *  startPlayback() is called, and only makes sense to do so (but is
109     *  not required) after putSamples() is called.  Flushing an active audio
110     *  buffer is likely to cause a discontinuity in the data, resulting in a
111     *  perceptible click.
112     *  <p>
113     *  Note that only the object with the exclusive lock on the playback audio
114     *  resources is allowed to invoke this method. An exception will occur if
115     *  the specified object does not have the lock on the playback audio
116     *  resources.
117     *
118     *  @param producer The object that has an exclusive lock on
119     *   the playback audio resources.
120     *
121     *  @exception IllegalStateException If audio playback is currently
122     *  inactive. That is, if startPlayback() has not yet been called
123     *  or if stopPlayback() has already been called.
124     *
125     *  @exception IOException If the calling program does not have permission
126     *  to access the audio playback resources.
127     */
128    @Override
129    public void flushPlaybackBuffer(Object producer)
130            throws IOException, IllegalStateException {
131        _flushPlaybackBuffer();
132    }
133
134    /** Return the number of bits per audio sample, which is
135     *  set by the setBitsPerSample() method. The default
136     *  value of this parameter is 16 bits.
137     *
138     * @return The sample size in bits.
139     * @see #setBitsPerSample(int)
140     */
141
142    /** Return the size of the internal capture audio buffer, in samples per
143     *  channel.
144     *
145     *  @return The internal buffer size in samples per channel.
146     *
147     *  @exception IllegalStateException If audio capture is inactive.
148     */
149    @Override
150    public int getBufferSizeCapture() throws IllegalStateException {
151        if (_targetLine != null) {
152            return _targetLine.getBufferSize() / (_bytesPerSample * _channels);
153        } else {
154            throw new IllegalStateException("LiveSound: "
155                    + "getBufferSizeCapture(), capture is probably inactive."
156                    + "Try to startCapture().");
157        }
158    }
159
160    /** Return the size of the internal playback audio buffer, in samples per
161     *  channel. This may differ from the requested buffer size if the hardware
162     *  does not support the requested buffer size. If playback has not
163     *  been started, then will simply return the requested buffer size.
164     *  @return The internal buffer size in samples per channel.
165     *  @exception IllegalStateException If audio playback is inactive.
166     */
167    @Override
168    public int getBufferSizePlayback() {
169        if (_sourceLine != null) {
170            return _sourceLine.getBufferSize() / (_bytesPerSample * _channels);
171        } else {
172            return _bufferSize;
173        }
174    }
175
176    /** Return an array of captured audio samples. This method
177     *  should be repeatedly called to obtain audio data.
178     *  The returned audio samples will have values in the range
179     *  [-1, 1], regardless of the audio bit resolution (bits per
180     *  sample).  This method should be called often enough to
181     *  prevent overflow of the internal audio buffer. If
182     *  overflow occurs, some audio data will be lost but no
183     *  exception or other error condition will occur. If
184     *  the audio data is not yet available, then this method
185     *  will block until the data is available.
186     *  <p>
187     *  The first index of the returned array
188     *  represents the channel number (0 for first channel, 1 for
189     *  second channel). The number of channels is set by the
190     *  setChannels() method. The second index represents the
191     *  sample index within a channel. For example,
192     *  <i>returned array</i>[n][m] contains the (m+1)th sample
193     *  of the (n+1)th channel. For each channel, n, the length of
194     *  <i>returned array</i>[n] is equal to the value returned by
195     *  the getTransferSize() method.
196     *  The size of the 2nd dimension of the returned array
197     *  is set by the setTransferSize() method.
198     *  <p>
199     *  Note that only the object with the exclusive lock on
200     *  the captured audio resources is allowed to invoked this
201     *  method. An exception will occur if the specified object
202     *  does not have the lock on the captured audio resources.
203     *
204     *  @param consumer The object that has an exclusive lock on
205     *   the capture audio resources.
206     *
207     *  @return Two dimensional array of captured audio samples.
208     *
209     *  @exception IllegalStateException If audio capture is currently
210     *   inactive.  That is, if startCapture() has not yet been called or if
211     *   stopCapture() has already been called.
212     *
213     *  @exception IOException If the calling program does not have permission
214     *   to access the audio capture resources.
215     */
216    @Override
217    public double[][] getSamples(Object consumer)
218            throws IOException, IllegalStateException {
219        if (!isCaptureActive()) {
220            throw new IllegalStateException("Object: " + consumer.toString()
221                    + " attempted to call LiveSound.getSamples(), but "
222                    + "capture is inactive.  Try to startCapture().");
223        }
224
225        if (!_soundConsumers.contains(consumer)) {
226            throw new IOException("Object: " + consumer.toString()
227                    + " attempted to call LiveSound.getSamples(), but "
228                    + "this object does not have permission to access the "
229                    + "audio capture resource.");
230        }
231
232        // Real-time capture.
233        int numBytesRead = _targetLine.read(_captureData, 0,
234                _captureData.length);
235
236        // Check if we need to reallocate.
237        if (_channels != _audioInDoubleArray.length
238                || _transferSize != _audioInDoubleArray[0].length) {
239            // Reallocate
240            _audioInDoubleArray = new double[_channels][_transferSize];
241        }
242
243        if (numBytesRead == _captureData.length) {
244            // Convert byte array to double array.
245            _byteArrayToDoubleArray(_audioInDoubleArray, _captureData);
246            return _audioInDoubleArray;
247        } else {
248            throw new IOException("Failed to capture correct number of bytes");
249        }
250    }
251
252    /** Play an array of audio samples. There will be a
253     *  delay before the audio data is actually heard, since the
254     *  audio data in <i>samplesArray</i> is queued to an
255     *  internal audio buffer. The setBufferSize() method suggests a size
256     *  for the internal buffer. An upper bound
257     *  on the latency is given by (<i>bufferSize</i> /
258     *  <i>sampleRate</i>) seconds. This method should be invoked often
259     *  enough to prevent underflow of the internal audio buffer.
260     *  Underflow is undesirable since it will cause audible gaps
261     *  in audio playback, but no exception or error condition will
262     *  occur. If the caller attempts to write more data than can
263     *  be written, this method blocks until the data can be
264     *  written to the internal audio buffer.
265     *  <p>
266     *  The samples should be in the range (-1, 1). Samples that are
267     *  outside this range will be hard-clipped so that they fall
268     *  within this range.
269     *  <p>
270     *  The first index of the specified array
271     *  represents the channel number (0 for first channel, 1 for
272     *  second channel, etc.). The number of channels is set by the
273     *  setChannels() method. The second index represents the
274     *  sample index within a channel. For example,
275     *  putSamplesArray[n][m] contains the (m+1)th sample
276     *  of the (n+1)th channel.
277     *  <p>
278     *  Note that only the object with the exclusive lock on
279     *  the playback audio resources is allowed to invoke this
280     *  method. An exception will occur if the specified object
281     *  does not have the lock on the playback audio resources.
282     *
283     *  @param producer The object that has an exclusive lock on
284     *   the playback audio resources.
285     *
286     *  @param samplesArray A two dimensional array containing
287     *  the samples to play or write to a file.
288     *
289     *  @exception IOException If the calling program does not have permission
290     *  to access the audio playback resources.
291     *
292     *  @exception IllegalStateException If audio playback is currently
293     *  inactive. That is, If startPlayback() has not yet been called
294     *  or if stopPlayback() has already been called.
295     */
296    @Override
297    public void putSamples(Object producer, double[][] samplesArray)
298            throws IOException, IllegalStateException {
299        if (!isPlaybackActive()) {
300            throw new IllegalStateException("Object: " + producer.toString()
301                    + " attempted to call LiveSound.putSamples(), but "
302                    + "playback is inactive.  Try to startPlayback().");
303        }
304        // Convert array of double valued samples into
305        // the proper byte array format.
306        byte[] playbackData = _doubleArrayToByteArray(samplesArray);
307
308        // Now write the array to output device.
309        int written = _sourceLine.write(playbackData, 0, playbackData.length);
310
311        if (written != playbackData.length) {
312            System.out.println("dropped samples!");
313        }
314    }
315
316    /** Stop audio capture. If audio capture is already inactive,
317     *  then do nothing. This method should generally not be used,
318     *  but it may be needed to turn of audio capture for the
319     *  case where an ill-behaved application exits without calling
320     *  stopCapture(). The preferred way of stopping audio capture
321     *  is by calling the stopCapture() method.
322     *
323     */
324    @Override
325    public void resetCapture() {
326        if (_targetLine != null) {
327            if (_targetLine.isOpen() == true) {
328                _targetLine.stop();
329                _targetLine.close();
330                _targetLine = null;
331            }
332        }
333
334        _captureIsActive = false;
335    }
336
337    /** Stop audio playback. If audio playback is already inactive,
338     *  then do nothing. This method should generally not be used,
339     *  but it may be needed to turn of audio playback for the
340     *  case where an ill-behaved application exits without calling
341     *  stopPlayback(). The preferred way of stopping audio playback
342     *  is by calling the stopPlayback() method.
343     *
344     */
345    @Override
346    public void resetPlayback() {
347        _stopPlayback();
348        _playbackIsActive = false;
349    }
350
351    /** Set the number of bits per sample to use for audio capture
352     *  and playback and notify any registered listeners of the change.
353     *  Allowable values include 8 and 16 bits. If
354     *  this method is not invoked, then the default value of 16
355     *  bits is used.
356     *  @param bitsPerSample The number of bits per sample.
357     *  @exception IOException If the specified bits per sample is
358     *   not supported by the audio hardware or by Java.
359     *  @see #getBitsPerSample()
360     */
361    @Override
362    public void setBitsPerSample(int bitsPerSample) throws IOException {
363        _bitsPerSample = bitsPerSample;
364        // FIXME: The following is wrong. Probably should just set bytes per sample.
365        _bytesPerSample = _bitsPerSample / 8;
366        // Note: _maxSample is maximum positive number, which ensures
367        // that maximum negative number is also in range.
368        switch (_bytesPerSample) {
369        case 1:
370            _maxSampleReciprocal = 1.0 / 128;
371            _maxSample = 127;
372            break;
373        case 2:
374            _maxSampleReciprocal = 1.0 / 32768;
375            _maxSample = 32767;
376            break;
377        case 3:
378            _maxSampleReciprocal = 1.0 / 8388608;
379            _maxSample = 8388607;
380            break;
381        case 4:
382            _maxSampleReciprocal = 1.0 / 147483648e9;
383            _maxSample = 147483647e9;
384            break;
385        default:
386            // Should not happen.
387            _maxSampleReciprocal = 0;
388        }
389
390        if (_captureIsActive && _playbackIsActive) {
391            // Restart capture/playback with new bitsPerSample.
392            _stopCapture();
393            _stopPlayback();
394            _startCapture();
395            _startPlayback();
396        } else if (_captureIsActive) {
397            // Restart capture with new bitsPerSample.
398            _stopCapture();
399            _startCapture();
400        } else if (_playbackIsActive) {
401            // Restart playback with new bitsPerSample.
402            _stopPlayback();
403            _startPlayback();
404        }
405
406        // Notify listeners of the change.
407        _notifyLiveSoundListeners(LiveSoundEvent.BITS_PER_SAMPLE);
408    }
409
410    /** Request that the internal capture and playback
411     *  audio buffers have bufferSize samples per channel and notify the
412     *  registered listeners of the change. If this method
413     *  is not invoked, the default value of 1024 is used.
414     *
415     *  @param bufferSize The suggested size of the internal capture and
416     *   playback audio buffers, in samples per channel.
417     *  @exception IOException If the specified number of channels is
418     *   not supported by the audio hardware or by Java.
419     *  @see #getBufferSize()
420     */
421    @Override
422    public void setBufferSize(int bufferSize) throws IOException {
423        _bufferSize = bufferSize;
424        if (_captureIsActive && _playbackIsActive) {
425            // Restart capture/playback with new bufferSize.
426            _stopCapture();
427            _stopPlayback();
428            _startCapture();
429            _startPlayback();
430        } else if (_captureIsActive) {
431            // Restart capture with new bufferSize.
432            _stopCapture();
433            _startCapture();
434        } else if (_playbackIsActive) {
435            // Restart playback with new bufferSize.
436            _stopPlayback();
437            _startPlayback();
438        }
439        // Notify listeners of the change.
440        _notifyLiveSoundListeners(LiveSoundEvent.BUFFER_SIZE);
441    }
442
443    /** Set the number of audio channels to use for capture and
444     *  playback and notify any registered listeners of the change.
445     *  Allowable values are 1 (for mono) and 2 (for
446     *  stereo). If this method is not invoked, the default
447     *  value of 1 audio channel is used. Note that this method
448     *  sets the size of the first dimension of the
449     *  2-dimensional array used by the putSamples() and
450     *  getSamples() methods.
451     *
452     *  @param channels The number audio channels.
453     *
454     *  @exception IOException If the specified number of channels is
455     *   not supported by the audio hardware or by Java.
456     *  @see #getChannels()
457     */
458    @Override
459    public void setChannels(int channels) throws IOException {
460        _channels = channels;
461        if (_captureIsActive && _playbackIsActive) {
462            // Restart capture/playback with new number of channels.
463            _stopCapture();
464            _stopPlayback();
465            _startCapture();
466            _startPlayback();
467        } else if (_captureIsActive) {
468            // Restart capture with new number of channels.
469            _stopCapture();
470            _startCapture();
471        } else if (_playbackIsActive) {
472            // Restart playback with new number of channels.
473            _stopPlayback();
474            _startPlayback();
475        }
476        // Notify listeners of the change.
477        _notifyLiveSoundListeners(LiveSoundEvent.CHANNELS);
478    }
479
480    /** Set the sample rate to use for audio capture and playback
481     *  and notify an registered listeners of the change.
482     *  Allowable values for this parameter are 8000, 11025,
483     *  22050, 44100, and 48000 Hz. If this method is not invoked,
484     *  then the default value of 8000 Hz is used.
485     *
486     *  @param sampleRate Sample rate in Hz.
487     *
488     *  @exception IOException If the specified sample rate is
489     *   not supported by the audio hardware or by Java.
490     *  @see #getSampleRate()
491     */
492    @Override
493    public void setSampleRate(int sampleRate) throws IOException {
494        _sampleRate = sampleRate;
495        if (_captureIsActive && _playbackIsActive) {
496            // Restart capture/playback with new sample rate.
497            _stopCapture();
498            _stopPlayback();
499            _startCapture();
500            _startPlayback();
501        } else if (_captureIsActive) {
502            // Restart capture with new sample rate.
503            _stopCapture();
504            _startCapture();
505        } else if (_playbackIsActive) {
506            // Restart playback with new sample rate.
507            _stopPlayback();
508            _startPlayback();
509        }
510        // Notify listeners of the change.
511        _notifyLiveSoundListeners(LiveSoundEvent.SAMPLE_RATE);
512    }
513
514    /** Start audio capture. The specified object will be
515     *  given an exclusive lock on the audio capture resources
516     *  until the stopCapture() method is called with the
517     *  same object reference. After this method returns,
518     *  the getSamples() method may be repeatedly invoked
519     *  (using the object reference as a parameter) to
520     *  capture audio.
521     *  <p>
522     *  If audio capture is already active, then an
523     *  exception will occur.
524     *
525     *  @param consumer The object to be given exclusive access
526     *   to the captured audio resources.
527     *
528     *  @exception IOException If another object currently has access
529     *   to the audio capture resources or if starting the capture or
530     *   playback throws it.
531     *
532     *  @exception IllegalStateException If this method is called
533     *   while audio capture is already active.
534     */
535    @Override
536    public void startCapture(Object consumer)
537            throws IOException, IllegalStateException {
538        // FIXME: consider allowing several object to
539        // share the captured audio resources.
540        if (_soundConsumers.size() > 0) {
541            throw new IOException("Object: " + consumer.toString()
542                    + " is not allowed to start audio capture because "
543                    + "another object currently has access to the audio "
544                    + "capture resources.");
545        }
546
547        if (!_soundConsumers.contains(consumer)) {
548            _soundConsumers.add(consumer);
549        } else {
550            throw new IllegalStateException("Object: " + consumer.toString()
551                    + " attempted to call LiveSound.startCapture() while "
552                    + "audio capture was active.");
553        }
554        // This is a workaround for a javasound bug. In javasound,
555        // when doing simultaneous capture and playback, the
556        // capture process must be started first. So, if
557        // there is already a playback process running then
558        // stop it before starting capture.
559        if (isPlaybackActive()) {
560            _stopPlayback();
561            _startCapture();
562            _startPlayback();
563        } else {
564            _startCapture();
565        }
566
567        _captureIsActive = true;
568    }
569
570    /** Start audio playback. The specified object will be
571     *  given an exclusive lock on the audio playback resources
572     *  until the stopPlayback() method is called with the
573     *  same object reference. After this method returns,
574     *  the putSamples() method may be repeatedly invoked
575     *  (using the object reference as a parameter) to
576     *  playback audio.
577     *  <p>
578     *  If audio playback is already active, then an
579     *  exception will occur.
580     *
581     *  @param producer The object to be given exclusive access
582     *   to the playback resources.
583     *
584     *  @exception IOException If another object currently has access
585     *   to the audio capture resources or if starting the playback throws it.
586     *
587     *  @exception IllegalStateException If this method is called
588     *   while audio playback is already active.
589     */
590    @Override
591    public void startPlayback(Object producer)
592            throws IOException, IllegalStateException {
593        if (!_playbackIsActive) {
594            _startPlayback();
595            _playbackIsActive = true;
596        }
597    }
598
599    /** Stop audio capture. If the specified object has
600     *  the lock on audio capture when this method is
601     *  invoked, then stop audio capture. Otherwise
602     *  an exception will occur.
603     *
604     *  @param consumer The object that held on exclusive
605     *   lock on the captured audio resources when this
606     *   method was invoked.
607     *
608     *  @exception IOException If another object currently has access
609     *   to the audio capture resources or if stopping the capture throws it.
610     *
611     *  @exception IllegalStateException If the specified
612     *   object did not hold an exclusive lock on the
613     *   captured audio resources when this method was invoked.
614     */
615    @Override
616    public void stopCapture(Object consumer)
617            throws IOException, IllegalStateException {
618        if (_soundConsumers.contains(consumer)) {
619            _soundConsumers.remove(consumer);
620        } else {
621            throw new IOException("Object: " + consumer.toString()
622                    + " attempted to call LiveSound.stopCapture(), but "
623                    + "never called LiveSound.startCapture().");
624        }
625
626        // Free up audio system resources.
627        _stopCapture();
628        _captureIsActive = false;
629    }
630
631    /** Stop audio playback. If the specified object has
632     *  the lock on audio playback when this method is
633     *  invoked, then stop audio playback. Otherwise
634     *  an exception will occur.
635     *
636     *  @param producer The object that held on exclusive
637     *   lock on the playback audio resources when this
638     *   method was invoked.
639     *
640     *  @exception IOException If another object currently has access
641     *   to the audio capture resources or if stopping the playback throws it.
642
643     *  @exception IllegalStateException If the specified
644     *   object did not hold an exclusive lock on the
645     *   playback audio resources when this method was invoked.
646     *
647     */
648    @Override
649    public void stopPlayback(Object producer)
650            throws IOException, IllegalStateException {
651        if (_playbackIsActive) {
652            _stopPlayback();
653        }
654        _playbackIsActive = false;
655    }
656
657    ///////////////////////////////////////////////////////////////////
658    ////                         private methods                   ////
659
660    /** Return a string that describes the possible encodings for an
661     *  AudioFormat.
662     *  @param format The audio format.
663     *  @return A string describing the audio formats available.
664     */
665    private String _encodings(AudioFormat format) {
666        // Print out the possible encodings
667        AudioFormat.Encoding[] encodings = AudioSystem
668                .getTargetEncodings(format);
669        StringBuffer encodingDescriptions = new StringBuffer();
670        for (Encoding encoding : encodings) {
671            encodingDescriptions.append(encoding + "\n");
672            AudioFormat[] formats = AudioSystem.getTargetFormats(encoding,
673                    format);
674            for (AudioFormat format2 : formats) {
675                encodingDescriptions.append("  " + format2 + "\n");
676            }
677        }
678        return encodingDescriptions.toString();
679    }
680
681    private void _flushCaptureBuffer() {
682        _targetLine.flush();
683    }
684
685    private void _flushPlaybackBuffer() {
686        _sourceLine.flush();
687    }
688
689    /** Start audio capture.
690     */
691    private void _startCapture() throws IOException {
692        boolean signed = true;
693        boolean bigEndian = true;
694        AudioFormat format = new AudioFormat(_sampleRate, _bitsPerSample,
695                _channels, signed, bigEndian);
696
697        DataLine.Info targetInfo = new DataLine.Info(TargetDataLine.class,
698                format, AudioSystem.NOT_SPECIFIED);
699
700        try {
701            _targetLine = (TargetDataLine) AudioSystem.getLine(targetInfo);
702
703            // Note: 2nd parameter is the buffer size (in bytes).
704            // Larger values increase latency but may be required if
705            // garbage collection, etc. is an issue.
706            _targetLine.open(format, _bufferSize * _bytesPerSample * _channels);
707        } catch (IllegalArgumentException ex) {
708            IOException exception = new IOException(
709                    "Incorrect argument, possible encodings for\n" + format
710                            + "\n are:\n" + _encodings(format));
711            exception.initCause(ex);
712            throw exception;
713        } catch (LineUnavailableException ex2) {
714            throw new IOException("Unable to open the line for "
715                    + "real-time audio capture: " + ex2);
716        }
717
718        // Array of audio samples in byte format.
719        _captureData = new byte[_transferSize * _bytesPerSample * _channels];
720        _audioInDoubleArray = new double[_channels][_transferSize];
721
722        // Start the target data line
723        _targetLine.start();
724    }
725
726    /** Start audio playback.
727     */
728    private void _startPlayback() throws IOException {
729        boolean signed = true;
730        boolean bigEndian = true;
731
732        AudioFormat format = new AudioFormat(_sampleRate, _bitsPerSample,
733                _channels, signed, bigEndian);
734
735        // As of Java 5.0, no longer need to specify this.
736        // Use the convenience method AudioSystem.getSourceDataLine(AudioFormat).
737        // DataLine.Info sourceInfo = new DataLine.Info(SourceDataLine.class,
738        //        format, AudioSystem.NOT_SPECIFIED);
739
740        // Get and open the source data line for playback.
741        try {
742            // Source DataLine is really a target for
743            // audio data, not a source.
744            _sourceLine = AudioSystem.getSourceDataLine(format);
745
746            // Open line and suggest a buffer size (in bytes) to use or
747            // the internal audio buffer.
748            _sourceLine.open(format, _bufferSize * _bytesPerSample * _channels);
749        } catch (IllegalArgumentException ex) {
750            IOException exception = new IOException(
751                    "Incorrect argument, possible encodings for\n" + format
752                            + "\n are:\n" + _encodings(format));
753            exception.initCause(ex);
754            throw exception;
755        } catch (LineUnavailableException ex) {
756            throw new IOException("Unable to open the line for "
757                    + "real-time audio playback: " + ex);
758        }
759        // Start the source data line
760        _sourceLine.start();
761    }
762
763    /** Stop audio playback.
764     */
765    private void _stopPlayback() {
766        if (_sourceLine != null) {
767            _sourceLine.drain();
768            _sourceLine.stop();
769            _sourceLine.close();
770        }
771
772        _sourceLine = null;
773    }
774
775    /** Stop audio capture.
776     */
777    private void _stopCapture() {
778        if (_targetLine != null) {
779            if (_targetLine.isOpen() == true) {
780                _targetLine.stop();
781                _targetLine.close();
782                _targetLine = null;
783            }
784        }
785    }
786
787    ///////////////////////////////////////////////////////////////////
788    ////                         private variables                 ////
789
790    /** Interface to the hardware for reading data. */
791    private SourceDataLine _sourceLine;
792
793    /** Interface to the hardware for producing sound. */
794    private TargetDataLine _targetLine;
795
796}