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}