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