001/* A buffer supporting the capturing of audio samples from a file or 002 from the computer's audio input port. 003 004 Copyright (c) 2000-2015 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; 032import java.net.URL; 033 034import javax.sound.sampled.AudioFormat; 035import javax.sound.sampled.AudioInputStream; 036import javax.sound.sampled.AudioSystem; 037import javax.sound.sampled.DataLine; 038import javax.sound.sampled.LineUnavailableException; 039import javax.sound.sampled.TargetDataLine; 040import javax.sound.sampled.UnsupportedAudioFileException; 041 042/////////////////////////////////////////////////////////////////// 043//// SoundCapture 044 045/** 046 A buffer supporting the capturing of audio samples from a file or 047 from the computer's audio input port. 048 049 <h2>Overview</h2> 050 051 <p>A buffer supporting the capturing of audio samples from a file or 052 from the computer's audio input port. This class supports the 053 real-time capture of audio from the audio input port (mic or line-in) 054 as well as the capture of audio from a sound file specified as a 055 URL. Single channel (mono) and multichannel audio (stereo) are 056 supported. This class, along with SoundPlayback, intends to provide 057 an easy to use interface to Java Sound, Java's audio API. Java Sound 058 supports the capture of audio data, but only at the byte level, which 059 is audio format specific. This class, however, provides higher level 060 support for the capture of double or integer valued samples from the 061 computer's audio input port or any supported sound file type. This 062 class is therefore useful when it one desires to capture audio 063 samples in an audio format independent way.</p> 064 065 <p>Depending on available audio system resources, it may be possible 066 to run an instance of this class and an instance of SoundPlayback 067 concurrently. This allows for the concurrent capture, signal 068 processing, and playback of audio data.</p> 069 070 <h2>Usage</h2> 071 072 <p>Two constructors are provided. One constructor creates a sound 073 capture object that captures from the line-in or microphone port. 074 The operating system must be used to select between the microphone 075 and line-in. This cannot be done using Java. If this constructor is 076 used, there will be a small delay between the time that the audio 077 enters the microphone or line-in and the time that the corresponding 078 audio samples are available via getSamples() or getSamplesInt(). 079 This latency can be adjusted by setting the <i>bufferSize</i> 080 constructor parameter. Another constructor creates a sound capture 081 object that captures audio from a sound file specified as a URL.</p> 082 083 <p> After calling the appropriate constructor, startCapture() must be 084 called to initialize the audio system for capture. The getSamples() 085 or getSamplesInt() method should then be repeatedly invoked to obtain 086 audio data in the form of a multidimensional array of audio sample 087 values. getSamples() will return audio sample values in the range 088 [-1, 1]. getSamplesInt() will return audio samples in the range 089 (-2^(bits_per_sample/2), 2^(bits_per_sample/2)), where 090 bits_per_sample is the number of bits per sample. For the case where 091 audio is captured from the mic or line-in, it is important to invoke 092 getSamples() or getSamplesInt() often enough to prevent overflow of 093 the internal audio buffer. The size of the internal buffer is set in 094 the constructor. Note that it is possible (but probably not useful) 095 to interleave calls to getSamples() and getSamplesInt(). Finally, 096 after no more audio data is desired, stopCapture() should be called 097 to free up audio system resources.</p> 098 099 <h2>Security issues</h2> 100 101 <p> Applications have no restrictions on the 102 capturing or playback of audio. Applets, however, may only capture 103 audio from a file specified as a URL on the same machine as the 104 one the applet was loaded from. Applet code is not allowed to 105 read or write native files. The .java.policy file must be 106 modified to grant applets more privileges.</p> 107 108 @author Brian K. Vogel 109 @version $Id$ 110 @since Ptolemy II 1.0 111 @Pt.ProposedRating Yellow (vogel) 112 @Pt.AcceptedRating Yellow (cxh) 113 @see ptolemy.media.javasound.SoundPlayback 114 */ 115public class SoundCapture { 116 /** Construct a sound capture object that captures audio from a computer's 117 * audio input port. If this constructor is used, then it 118 * is important that getSamples() be 119 * invoked often enough to prevent overflow of the internal audio 120 * input buffer. Note the startCapture() must be called before the 121 * first invocation of getSamples(), otherwise getSamples() will 122 * throw an exception. 123 * @param sampleRate Sample rate in Hz. Must be in the range: 8000 124 * to 48000. 125 * @param sampleSizeInBits Number of bits per sample. Choices are 126 * 8 or 16. 127 * @param channels Number of audio channels. 1 for mono, 2 for 128 * stereo. 129 * @param bufferSize Requested size of the internal audio input 130 * buffer in samples. This controls the latency. A lower bound 131 * on the latency is given by (<i>bufferSize</i> / <i>sampleRate</i>) 132 * seconds. Ideally, the 133 * smallest value that gives acceptable performance (no overflow) 134 * should be used. Typical values are about 1/10 th the sample 135 * rate. For example, at 44100 Hz sample rate, a typical buffer 136 * size value might be 4410. 137 * @param getSamplesSize Size of the array returned by 138 * getSamples(). For performance reasons, the size should 139 * be chosen smaller than <i>bufferSize</i>. Typical values 140 * are 1/2 to 1/16th of <i>bufferSize</i>. 141 */ 142 public SoundCapture(float sampleRate, int sampleSizeInBits, int channels, 143 int bufferSize, int getSamplesSize) { 144 _isAudioCaptureActive = false; 145 146 // Set mode to real-time. 147 this._isRealTime = true; 148 this._sampleSizeInBits = sampleSizeInBits; 149 this._sampleRate = sampleRate; 150 this._channels = channels; 151 this._bufferSize = bufferSize; 152 this._productionRate = getSamplesSize; 153 } 154 155 /** Construct a sound capture object that captures audio from a 156 * sound file specified as a URL. Note that it is still possible 157 * to capture audio from a file on the local file system. For 158 * example, to capture from a sound file located at 159 * "C:\someDir\someFile.wave", <i>pathName</i> 160 * should be set to "file:///C:/someDir/someFile.wave". 161 * <p> 162 * Note the startCapture() must be called before the 163 * first invocation of getSamples(), otherwise getSamples() will 164 * throw an exception. 165 * 166 * @param pathName The name of the file as a URL. Valid sound file 167 * formats are WAVE (.wav), AIFF (.aif, .aiff), AU (.au). The file 168 * format is automatically determined from the file extension. 169 * If there is a problem reading the sound file, an IOException 170 * will be thrown in startCapture(). 171 * @param getSamplesSize The number of samples per channel 172 * returned by getSamples(). 173 */ 174 public SoundCapture(String pathName, int getSamplesSize) { 175 _isAudioCaptureActive = false; 176 177 // Set mode to "capture from file" (not real-time). 178 this._isRealTime = false; 179 this._pathName = pathName; 180 this._productionRate = getSamplesSize; 181 } 182 183 /////////////////////////////////////////////////////////////////// 184 /// Public Methods /// 185 186 /** Return the number of audio channels. This method will 187 * return the number of audio channels, regardless of 188 * which constructor was used. However, this method is 189 * really only useful when the constructor that causes 190 * audio to be captured from a file is used, since 191 * the number of channels is unknown until the file 192 * is opened. 193 * <p> 194 * This method should 195 * be called while audio capture is active, i.e., after 196 * startCapture() is called and before stopCapture() 197 * is called. 198 * 199 * @return The number of audio channels. Return null if 200 * this method is called before startCapture(). 201 * 202 * @exception IllegalStateException If this method is called 203 * before startCapture() is called or after stopCapture() 204 * is called. 205 */ 206 public int getChannels() throws IllegalStateException { 207 if (_isAudioCaptureActive == true) { 208 return _channels; 209 } 210 throw new IllegalStateException("SoundCapture: " 211 + "getChannels() was called while audio capture was" 212 + " inactive (startCapture() was never called)."); 213 } 214 215 /** Return the sampling rate in Hz. This method will 216 * return the sampling rate, regardless of 217 * which constructor was used. However, this method is 218 * really only useful when the constructor that causes 219 * audio to be captured from a file is used, since 220 * the sampling rate is unknown until the file 221 * is opened. 222 * <p> 223 * This method should 224 * be called while audio capture is active, i.e., after 225 * startCapture() is called and before stopCapture() 226 * is called. 227 * 228 * @return The sample rate in Hz. Return null if 229 * this method is called before startCapture(). 230 * 231 * @exception IllegalStateException If this method is called 232 * before startCapture() is called or after stopCapture() 233 * is called. 234 */ 235 public float getSampleRate() throws IllegalStateException { 236 if (_isAudioCaptureActive == true) { 237 return _sampleRate; 238 } 239 throw new IllegalStateException("SoundCapture: " 240 + "getSampleRate() was called while audio capture was" 241 + " inactive (startCapture() was never called)."); 242 } 243 244 /** Return an array of captured audio samples. This method 245 * should be repeatedly called to obtain audio data. 246 * The returned audio samples will have values in the range 247 * [-1, 1], regardless of the audio bit resolution (bits per 248 * sample). When 249 * capturing from the computer's audio input port (mic or 250 * line-in), this method should be called often enough to 251 * prevent overflow of the internal audio buffer. If 252 * overflow occurs, some audio data will be lost but no 253 * exception or other error condition will occur. If 254 * the audio data is not yet available, then this method 255 * will block until the data is available. When capturing 256 * from a sound file, it is not possible for overflow to 257 * occur. 258 * <p> 259 * The array size 260 * is set by the <i>getSamplesSize</i> parameter in the 261 * constructor. For the case where audio is captured from 262 * the computer's audio-in port (mic or line-in), this 263 * method should be called often enough to prevent overflow 264 * of the internal audio buffer, the size of which is set 265 * in the constructor. 266 * @return Two dimensional array of captured audio samples. 267 * Return null 268 * if end of audio file is reached. A null return value is 269 * only possible when capturing from a sound file. 270 * The first index 271 * represents the channel number (0 for first channel, 1 for 272 * second channel, etc.). The second index represents the 273 * sample index within a channel. For example, 274 * <i>returned array</i>[n][m] contains the (m+1)th sample 275 * of the (n+1)th channel. For each channel, n, the length of 276 * <i>returned array</i>[n] is equal to <i>getSamplesSize</i>. 277 * 278 * @exception IOException If there is a problem capturing audio. 279 * @exception IllegalStateException If audio capture is currently 280 * inactive. That is, If startCapture() has not yet been called 281 * or if stopCapture() has already been called. 282 */ 283 public double[][] getSamples() throws IOException, IllegalStateException { 284 if (_isAudioCaptureActive == true) { 285 int numBytesRead; 286 287 if (_isRealTime == true) { 288 // Real-time capture. 289 numBytesRead = _targetLine.read(_data, 0, 290 _productionRate * _frameSizeInBytes); 291 } else { 292 // Capture audio from file. 293 numBytesRead = _properFormatAudioInputStream.read(_data); 294 } 295 296 if (numBytesRead == _data.length) { 297 // Convert byte array to double array. 298 _audioInDoubleArray = _byteArrayToDoubleArray(_data, 299 _bytesPerSample, _channels); 300 return _audioInDoubleArray; 301 } else if (numBytesRead != _data.length) { 302 // Read fewer samples than productionRate many samples. 303 // FIXME: There appears to be a java sound bug that 304 // causes AudioInputStream.read(array) to sometimes 305 // return fewer bytes than requested, even though 306 // the end of the file has not yet been reached. 307 _audioInDoubleArray = _byteArrayToDoubleArray(_data, 308 _bytesPerSample, _channels); 309 return _audioInDoubleArray; 310 } else if (numBytesRead == -1) { 311 // Ran out of samples to play. This generally means 312 // that the end of the sound file has been reached. 313 return null; 314 } 315 316 return null; 317 } 318 throw new IllegalStateException("SoundCapture: " 319 + "getSamples() was called while audio capture was" 320 + " inactive (startCapture() was never called or " 321 + "stopCapture has already been called)."); 322 } 323 324 /** Return an array of captured audio samples. This method 325 * should be repeatedly called to obtain audio data. This 326 * method requires less computation than getSamples(), 327 * since no conversion to doubles is performed. Therefore, 328 * the use of this method is recommended when integer 329 * valued audio samples are sufficient. The 330 * returned audio samples will have values in the range 331 * (-2^(bits_per_sample/2), 2^(bits_per_sample/2)). The 332 * range of sample values returned is therefore dependent 333 * on the bit resolution of the audio data. If this is not 334 * desired, then use getSamples() instead. 335 * <p> 336 * When capturing from the computer's audio input port (mic or 337 * line-in), this method should be called often enough to 338 * prevent overflow of the internal audio buffer. If 339 * overflow occurs, some audio data will be lost but no 340 * exception or other error condition will occur. If 341 * the audio data is not yet available, then this method 342 * will block until the data is available. When capturing 343 * from a sound file, it is not possible for overflow to 344 * occur. 345 * <p> The array size 346 * is set by the <i>getSamplesSize</i> parameter in the 347 * constructor. For the case where audio is captured from 348 * the computer's audio-in port (mic or line-in), this 349 * method should be called often enough to prevent overflow 350 * of the internal audio buffer, the size of which is set 351 * in the constructor. 352 * @return Two dimensional array of captured audio samples. 353 * Return null 354 * if end of audio file is reached A null return value is 355 * only possible when capturing from a sound file. 356 * The first index 357 * represents the channel number (0 for first channel, 1 for 358 * second channel, etc.). The second index represents the 359 * sample index within a channel. For example, 360 * <i>returned array</i>[n][m] contains the (m+1)th sample 361 * of the (n+1)th channel. For each channel, n, the length of 362 * <i>returned array</i>[n] is equal to <i>getSamplesSize</i>. 363 * 364 * @exception IOException If there is a problem capturing audio. 365 * @exception IllegalStateException If audio capture is currently 366 * inactive. That is, If startCapture() has not yet been called 367 * or if stopCapture() has already been called. 368 */ 369 public int[][] getSamplesInt() throws IOException, IllegalStateException { 370 if (_isAudioCaptureActive == true) { 371 int numBytesRead; 372 373 if (_isRealTime == true) { 374 // Real-time capture. 375 numBytesRead = _targetLine.read(_data, 0, 376 _productionRate * _frameSizeInBytes); 377 } else { 378 // Capture audio from file. 379 numBytesRead = _properFormatAudioInputStream.read(_data); 380 } 381 382 if (numBytesRead == _data.length) { 383 // Convert byte array to double array. 384 _audioInIntArray = _byteArrayToIntArray(_data, _bytesPerSample, 385 _channels); 386 return _audioInIntArray; 387 } else if (numBytesRead != _data.length) { 388 // Read fewer samples than productionRate many samples. 389 // FIXME: Output the samples that were read + zeros? 390 return null; 391 } else if (numBytesRead == -1) { 392 // Ran out of samples to play. This generally means 393 // that the end of the sound file has been reached. 394 return null; 395 } 396 397 return null; 398 } 399 throw new IllegalStateException("SoundCapture: " 400 + "getSamples() was called while audio capture was" 401 + " inactive (startCapture() was never called or " 402 + "stopCapture has already been called)."); 403 } 404 405 /** Begin capturing audio. This method must be invoked prior 406 * to the first invocation of getSamples(). If this is not 407 * done, then getSamples() will throw an exception when 408 * it is invoked. It is safe 409 * to call getSamples() immediately after this method returns. 410 * This method must not be called more than 411 * once between invocations of stopCapture(). Calling 412 * this method more than once between invocations of 413 * stopCapture() will cause this method to throw an exception. 414 * 415 * @exception IOException If there is a problem setting up 416 * the system for audio capture. This will occur if the 417 * a URL cannot be opened or if the audio in port cannot 418 * be accessed. 419 * @exception IllegalStateException If this method is called 420 * more than once between invocations of stopCapture(). 421 */ 422 public void startCapture() throws IOException, IllegalStateException { 423 if (_isAudioCaptureActive == false) { 424 // FIXME: check and throw Exceptions 425 if (_isRealTime == true) { 426 _startCaptureRealTime(); 427 } else { 428 _startCaptureFromFile(); 429 } 430 431 _isAudioCaptureActive = true; 432 } else { 433 throw new IllegalStateException("SoundCapture: " 434 + "startCapture() was called while audio capture was" 435 + " already active (startCapture() was called " 436 + "more than once between invocations of stopCapture())."); 437 } 438 } 439 440 /** Stop capturing audio. This method should be called when 441 * no more calls to getSamples(). are required, so 442 * that the system resources involved in the audio capture 443 * may be freed. 444 * 445 * @exception IOException If there is a problem closing the 446 * audio resources. 447 */ 448 public void stopCapture() throws IOException { 449 if (_isAudioCaptureActive == true) { 450 // Free up audio system resources. 451 // For capture from file: 452 if (_audioInputStream != null) { 453 _audioInputStream.close(); 454 455 // FIXME : is this correct? 456 _audioInputStream = null; 457 } 458 459 if (_properFormatAudioInputStream != null) { 460 _properFormatAudioInputStream.close(); 461 462 // FIXME : is this correct? 463 _properFormatAudioInputStream = null; 464 } 465 466 // For real-time capture: 467 if (_targetLine != null) { 468 if (_targetLine.isOpen() == true) { 469 _targetLine.stop(); 470 _targetLine.close(); 471 _targetLine = null; 472 } 473 } 474 } 475 476 _isAudioCaptureActive = false; 477 } 478 479 /** Return the number of bits per audio sample. This method will 480 * return the number of bits per audio sample, regardless of 481 * which constructor was used. However, this method is 482 * really only useful when the constructor that causes 483 * audio to be captured from a file is used, since 484 * the number of bits per audio sample is unknown until the file 485 * is opened. 486 * <p> 487 * This method must 488 * be called while audio capture is active, i.e., after 489 * startCapture() is called and before stopCapture() 490 * is called, or else an exception will be thrown. 491 * 492 * @return The sample size in bits. Return null if 493 * this method is called before startCapture(). 494 * 495 * @exception IllegalStateException If this method is called 496 * before startCapture() is called or after stopCapture() 497 * is called. 498 */ 499 public int getSampleSizeInBits() throws IllegalStateException { 500 if (_isAudioCaptureActive == true) { 501 return _sampleSizeInBits; 502 } 503 throw new IllegalStateException("SoundCapture: " 504 + "getSampleSizeInBits() was called while audio capture was" 505 + " inactive (startCapture() was never called)."); 506 } 507 508 /////////////////////////////////////////////////////////////////// 509 //// private methods //// 510 private void _startCaptureRealTime() throws IOException { 511 boolean signed = true; 512 boolean bigEndian = true; 513 514 AudioFormat format = new AudioFormat(_sampleRate, _sampleSizeInBits, 515 _channels, signed, bigEndian); 516 517 _frameSizeInBytes = format.getFrameSize(); 518 519 DataLine.Info targetInfo = new DataLine.Info(TargetDataLine.class, 520 format, AudioSystem.NOT_SPECIFIED); 521 522 // The following works under Windows Java 1.3.0 RC2 but 523 // not under Tritonus under Linux, so comment out. 524 //if (!AudioSystem.isLineSupported(targetInfo)) { 525 // // FIXME: throw exception here. 526 // System.out.println("Line matching " + targetInfo + 527 // " not supported."); 528 // return; 529 //} 530 try { 531 _targetLine = (TargetDataLine) AudioSystem.getLine(targetInfo); 532 533 // Note: 2nd parameter is the buffer size (in bytes). 534 // Larger values increase latency but may be required if 535 // garbage collection, etc. is an issue. 536 _targetLine.open(format, _bufferSize * _frameSizeInBytes); 537 } catch (LineUnavailableException ex) { 538 throw new IOException("Unable to open the line for " 539 + "real-time audio capture: " + ex); 540 } 541 542 // The following works under Windows Java 1.3.0 RC2 but 543 // not under Tritonus under Linux, so comment out. 544 //if (!AudioSystem.isLineSupported(sourceInfo)) { 545 // //FIXME: handle this correctly. 546 // System.err.println("Line matching " + sourceInfo + 547 // " not supported."); 548 // return; 549 //} 550 // Array of audio samples in byte format. 551 _data = new byte[_productionRate * _frameSizeInBytes]; 552 553 _bytesPerSample = _sampleSizeInBits / 8; 554 555 // Start the target data line 556 _targetLine.start(); 557 } 558 559 /* Perform necessary initialization to capture from a sound 560 * file. The sound file is specified as a URL. 561 */ 562 private void _startCaptureFromFile() throws IOException { 563 // Load audio from a URL. 564 // Create a URL corresponding to the sound file location. 565 URL soundURL = new URL(_pathName); 566 567 if (soundURL != null) { 568 try { 569 _audioInputStream = AudioSystem.getAudioInputStream(soundURL); 570 } catch (UnsupportedAudioFileException e) { 571 throw new IOException("Unsupported AudioFile :" + e); 572 } 573 } 574 575 // make sure we have something to play 576 if (_audioInputStream == null) { 577 throw new IOException("No loaded audio to play back"); 578 } 579 580 // FIXME: is this correct? 581 //_audioInputStream.reset(); 582 AudioFormat origFormat = _audioInputStream.getFormat(); 583 584 // Now convert to PCM_SIGNED_BIG_ENDIAN so that can get double 585 // representation of samples. 586 float sampleRate = origFormat.getSampleRate(); 587 588 _sampleSizeInBits = origFormat.getSampleSizeInBits(); 589 _bytesPerSample = _sampleSizeInBits / 8; 590 591 _channels = origFormat.getChannels(); 592 593 boolean signed = true; 594 boolean bigEndian = true; 595 AudioFormat format = new AudioFormat(sampleRate, _sampleSizeInBits, 596 _channels, signed, bigEndian); 597 _properFormatAudioInputStream = AudioSystem.getAudioInputStream(format, 598 _audioInputStream); 599 600 _frameSizeInBytes = format.getFrameSize(); 601 602 // FIXME: is this correct? 603 //_properFormatAudioInputStream.reset(); 604 // Array of audio samples in byte format. 605 _data = new byte[_productionRate * _frameSizeInBytes]; 606 } 607 608 /* Convert a byte array of audio samples in linear signed pcm big endian 609 * format into a double array of audio samples (-1, 1) range. 610 * @param byteArray The linear signed pcm big endian byte array 611 * formatted array representation of audio data. 612 * @param bytesPerSample Number of bytes per sample. Supported 613 * bytes per sample by this method are 8, 16, 24, 32. 614 * @param channels Number of audio channels. 1 for mono, 2 for 615 * stereo. 616 * @return Two dimensional array holding audio samples. 617 * For each channel, m, doubleArray[m] is a single dimensional 618 * array containing samples for channel m. 619 */ 620 private double[][] _byteArrayToDoubleArray(byte[] byteArray, 621 int bytesPerSample, int channels) { 622 int lengthInSamples = byteArray.length / (bytesPerSample * channels); 623 624 // Check if we need to reallocate. 625 if (channels != _doubleArray.length 626 || lengthInSamples != _doubleArray[0].length) { 627 // Reallocate 628 _doubleArray = new double[channels][lengthInSamples]; 629 } 630 631 //double maxSampleReciprocal = 1/(Math.pow(2, 8 * bytesPerSample - 1)); 632 // Could use above line, but hopefully, code below will 633 // be faster. 634 double maxSampleReciprocal; 635 636 if (bytesPerSample == 2) { 637 // 1 / 32768 638 maxSampleReciprocal = 3.0517578125e-5; 639 } else if (bytesPerSample == 1) { // 1 / 128 640 maxSampleReciprocal = 7.8125e-3; 641 } else if (bytesPerSample == 3) { 642 // 1 / 8388608 643 maxSampleReciprocal = 1.1920928955e07; 644 } else if (bytesPerSample == 4) { 645 // 1 / 147483648e9 646 maxSampleReciprocal = 4.655661287308e-10; 647 } else { 648 // Should not happen. 649 maxSampleReciprocal = 0; 650 } 651 652 // Check if we need to reallocate. 653 // FIXME: This test is really not needed since bytesPerSample 654 // is set in the constructor. It should never change. 655 if (bytesPerSample != _b.length) { 656 _b = new byte[bytesPerSample]; 657 } 658 659 for (int currSamp = 0; currSamp < lengthInSamples; currSamp++) { 660 // For each channel, 661 for (int currChannel = 0; currChannel < channels; currChannel++) { 662 for (int i = 0; i < bytesPerSample; i += 1) { 663 // Assume we are dealing with big endian. 664 _b[i] = byteArray[currSamp * bytesPerSample * channels 665 + bytesPerSample * currChannel + i]; 666 } 667 668 int result = _b[0] >> 7; 669 670 for (int i = 0; i < bytesPerSample; i += 1) { 671 result = (result << 8) + (_b[i] & 0xff); 672 } 673 674 _doubleArray[currChannel][currSamp] = result 675 * maxSampleReciprocal; 676 } 677 } 678 679 return _doubleArray; 680 } 681 682 /* Convert a byte array of audio samples in linear signed pcm big endian 683 * format into a (signed) int array of audio samples. The range 684 * of the returned samples is approximately 685 * (-2^(bits_per_sample/2), 2^(bits_per_sample/2)). 686 * @param byteArray The linear signed pcm big endian byte array 687 * formatted array representation of audio data. 688 * @param bytesPerSample Number of bytes per sample. Supported 689 * bytes per sample by this method are 8, 16, 24, 32. 690 * @param channels Number of audio channels. 1 for mono, 2 for 691 * stereo. 692 * @return Two dimensional array holding audio samples. 693 * For each channel, m, intArray[m] is a single dimensional 694 * array containing samples for channel m. 695 */ 696 private int[][] _byteArrayToIntArray(byte[] byteArray, int bytesPerSample, 697 int channels) { 698 int lengthInSamples = byteArray.length / (bytesPerSample * channels); 699 700 // Check if we need to reallocate. 701 if (channels != _doubleArray.length 702 || lengthInSamples != _doubleArray[0].length) { 703 // Reallocate 704 _intArray = new int[channels][lengthInSamples]; 705 } 706 707 // Check if we need to reallocate. 708 // FIXME: This test is really not needed since bytesPerSample 709 // is set in the constructor. It should never change. 710 if (bytesPerSample != _b.length) { 711 _b = new byte[bytesPerSample]; 712 } 713 714 for (int currSamp = 0; currSamp < lengthInSamples; currSamp++) { 715 // For each channel, 716 for (int currChannel = 0; currChannel < channels; currChannel++) { 717 for (int i = 0; i < bytesPerSample; i += 1) { 718 // Assume we are dealing with big endian. 719 _b[i] = byteArray[currSamp * bytesPerSample * channels 720 + bytesPerSample * currChannel + i]; 721 } 722 723 int result = _b[0] >> 7; 724 725 for (int i = 0; i < bytesPerSample; i += 1) { 726 result = (result << 8) + (_b[i] & 0xff); 727 } 728 729 _intArray[currChannel][currSamp] = result; 730 } 731 } 732 733 return _intArray; 734 } 735 736 /////////////////////////////////////////////////////////////////// 737 //// private variables //// 738 private AudioInputStream _properFormatAudioInputStream; 739 740 private AudioInputStream _audioInputStream; 741 742 private int _productionRate; 743 744 // Array of audio samples in double format. 745 private double[][] _audioInDoubleArray; 746 747 // Array of audio samples in int format. 748 private int[][] _audioInIntArray; 749 750 // Array of audio samples in byte format. 751 private byte[] _data; 752 753 private int _frameSizeInBytes; 754 755 private boolean _isRealTime; 756 757 private String _pathName; 758 759 private int _sampleSizeInBits; 760 761 private float _sampleRate; 762 763 private int _channels; 764 765 private int _bufferSize; 766 767 private TargetDataLine _targetLine; 768 769 private int _bytesPerSample; 770 771 private boolean _isAudioCaptureActive; 772 773 private byte[] _b = new byte[1]; 774 775 private double[][] _doubleArray = new double[1][1]; 776 777 private int[][] _intArray = new int[1][1]; 778}