/Users/lyon/j4p/src/sound/player/LiveSound.java

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