src/share/classes/javax/sound/sampled/AudioSystem.java

Print this page




  33 
  34 import java.util.HashSet;
  35 import java.util.List;
  36 import java.util.Set;
  37 import java.util.Vector;
  38 import java.util.ArrayList;
  39 
  40 import javax.sound.sampled.spi.AudioFileWriter;
  41 import javax.sound.sampled.spi.AudioFileReader;
  42 import javax.sound.sampled.spi.FormatConversionProvider;
  43 import javax.sound.sampled.spi.MixerProvider;
  44 
  45 import com.sun.media.sound.JDK13Services;
  46 
  47 /* $fb TODO:
  48  * - consistent usage of (typed) collections
  49  */
  50 
  51 
  52 /**
  53  * The <code>AudioSystem</code> class acts as the entry point to the
  54  * sampled-audio system resources. This class lets you query and
  55  * access the mixers that are installed on the system.
  56  * <code>AudioSystem</code> includes a number of
  57  * methods for converting audio data between different formats, and for
  58  * translating between audio files and streams. It also provides a method
  59  * for obtaining a <code>{@link Line}</code> directly from the
  60  * <code>AudioSystem</code> without dealing explicitly
  61  * with mixers.
  62  *
  63  * <p>Properties can be used to specify the default mixer
  64  * for specific line types.
  65  * Both system properties and a properties file are considered.
  66  * The <code>sound.properties</code> properties file is read from
  67  * an implementation-specific location (typically it is the <code>lib</code>
  68  * directory in the Java installation directory).
  69  * If a property exists both as a system property and in the
  70  * properties file, the system property takes precedence. If none is
  71  * specified, a suitable default is chosen among the available devices.
  72  * The syntax of the properties file is specified in
  73  * {@link java.util.Properties#load(InputStream) Properties.load}. The
  74  * following table lists the available property keys and which methods
  75  * consider them:
  76  *
  77  * <table border=0>
  78  *  <caption>Audio System Property Keys</caption>
  79  *  <tr>
  80  *   <th>Property Key</th>
  81  *   <th>Interface</th>
  82  *   <th>Affected Method(s)</th>
  83  *  </tr>
  84  *  <tr>
  85  *   <td><code>javax.sound.sampled.Clip</code></td>
  86  *   <td>{@link Clip}</td>
  87  *   <td>{@link #getLine}, {@link #getClip}</td>
  88  *  </tr>
  89  *  <tr>
  90  *   <td><code>javax.sound.sampled.Port</code></td>
  91  *   <td>{@link Port}</td>
  92  *   <td>{@link #getLine}</td>
  93  *  </tr>
  94  *  <tr>
  95  *   <td><code>javax.sound.sampled.SourceDataLine</code></td>
  96  *   <td>{@link SourceDataLine}</td>
  97  *   <td>{@link #getLine}, {@link #getSourceDataLine}</td>
  98  *  </tr>
  99  *  <tr>
 100  *   <td><code>javax.sound.sampled.TargetDataLine</code></td>
 101  *   <td>{@link TargetDataLine}</td>
 102  *   <td>{@link #getLine}, {@link #getTargetDataLine}</td>
 103  *  </tr>
 104  * </table>
 105  *
 106  * The property value consists of the provider class name
 107  * and the mixer name, separated by the hash mark (&quot;#&quot;).
 108  * The provider class name is the fully-qualified
 109  * name of a concrete {@link javax.sound.sampled.spi.MixerProvider
 110  * mixer provider} class. The mixer name is matched against
 111  * the <code>String</code> returned by the <code>getName</code>
 112  * method of <code>Mixer.Info</code>.
 113  * Either the class name, or the mixer name may be omitted.
 114  * If only the class name is specified, the trailing hash mark
 115  * is optional.
 116  *
 117  * <p>If the provider class is specified, and it can be
 118  * successfully retrieved from the installed providers, the list of
 119  * <code>Mixer.Info</code> objects is retrieved
 120  * from the provider. Otherwise, or when these mixers
 121  * do not provide a subsequent match, the list is retrieved
 122  * from {@link #getMixerInfo} to contain
 123  * all available <code>Mixer.Info</code> objects.
 124  *
 125  * <p>If a mixer name is specified, the resulting list of
 126  * <code>Mixer.Info</code> objects is searched:
 127  * the first one with a matching name, and whose
 128  * <code>Mixer</code> provides the
 129  * respective line interface, will be returned.
 130  * If no matching <code>Mixer.Info</code> object
 131  * is found, or the mixer name is not specified,
 132  * the first mixer from the resulting
 133  * list, which provides the respective line
 134  * interface, will be returned.
 135  *
 136  * For example, the property <code>javax.sound.sampled.Clip</code>
 137  * with a value
 138  * <code>&quot;com.sun.media.sound.MixerProvider#SunClip&quot;</code>
 139  * will have the following consequences when
 140  * <code>getLine</code> is called requesting a <code>Clip</code>
 141  * instance:
 142  * if the class <code>com.sun.media.sound.MixerProvider</code> exists
 143  * in the list of installed mixer providers,
 144  * the first <code>Clip</code> from the first mixer with name
 145  * <code>&quot;SunClip&quot;</code> will be returned. If it cannot
 146  * be found, the first <code>Clip</code> from the first mixer
 147  * of the specified provider will be returned, regardless of name.
 148  * If there is none, the first <code>Clip</code> from the first
 149  * <code>Mixer</code> with name
 150  * <code>&quot;SunClip&quot;</code> in the list of all mixers
 151  * (as returned by <code>getMixerInfo</code>) will be returned,
 152  * or, if not found, the first <code>Clip</code> of the first
 153  * <code>Mixer</code>that can be found in the list of all
 154  * mixers is returned.
 155  * If that fails, too, an <code>IllegalArgumentException</code>
 156  * is thrown.
 157  *
 158  * @author Kara Kytle
 159  * @author Florian Bomers
 160  * @author Matthias Pfisterer
 161  * @author Kevin P. Smith
 162  *
 163  * @see AudioFormat
 164  * @see AudioInputStream
 165  * @see Mixer
 166  * @see Line
 167  * @see Line.Info
 168  * @since 1.3
 169  */
 170 public class AudioSystem {
 171 
 172     /**
 173      * An integer that stands for an unknown numeric value.
 174      * This value is appropriate only for signed quantities that do not
 175      * normally take negative values.  Examples include file sizes, frame
 176      * sizes, buffer sizes, and sample rates.
 177      * A number of Java Sound constructors accept
 178      * a value of <code>NOT_SPECIFIED</code> for such parameters.  Other
 179      * methods may also accept or return this value, as documented.
 180      */
 181     public static final int NOT_SPECIFIED = -1;
 182 
 183     /**
 184      * Private no-args constructor for ensuring against instantiation.
 185      */
 186     private AudioSystem() {
 187     }
 188 
 189 
 190     /**
 191      * Obtains an array of mixer info objects that represents
 192      * the set of audio mixers that are currently installed on the system.
 193      * @return an array of info objects for the currently installed mixers.  If no mixers
 194      * are available on the system, an array of length 0 is returned.


 195      * @see #getMixer
 196      */
 197     public static Mixer.Info[] getMixerInfo() {
 198 
 199         List infos = getMixerInfoList();
 200         Mixer.Info[] allInfos = (Mixer.Info[]) infos.toArray(new Mixer.Info[infos.size()]);
 201         return allInfos;
 202     }
 203 
 204 
 205     /**
 206      * Obtains the requested audio mixer.
 207      * @param info a <code>Mixer.Info</code> object representing the desired
 208      * mixer, or <code>null</code> for the system default mixer

 209      * @return the requested mixer
 210      * @throws SecurityException if the requested mixer
 211      * is unavailable because of security restrictions
 212      * @throws IllegalArgumentException if the info object does not represent
 213      * a mixer installed on the system
 214      * @see #getMixerInfo
 215      */
 216     public static Mixer getMixer(Mixer.Info info) {
 217 
 218         Mixer mixer = null;
 219         List providers = getMixerProviders();
 220 
 221         for(int i = 0; i < providers.size(); i++ ) {
 222 
 223             try {
 224                 return ((MixerProvider)providers.get(i)).getMixer(info);
 225 
 226             } catch (IllegalArgumentException e) {
 227             } catch (NullPointerException e) {
 228                 // $$jb 08.20.99:  If the strings in the info object aren't
 229                 // set, then Netscape (using jdk1.1.5) tends to throw
 230                 // NPE's when doing some string manipulation.  This is
 231                 // probably not the best fix, but is solves the problem
 232                 // of the NPE in Netscape using local classes
 233                 // $$jb 11.01.99: Replacing this patch.


 242                     Mixer.Info[] infos = provider.getMixerInfo();
 243                     // start from 0 to last device (do not reverse this order)
 244                     for (int ii = 0; ii < infos.length; ii++) {
 245                         try {
 246                             return provider.getMixer(infos[ii]);
 247                         } catch (IllegalArgumentException e) {
 248                             // this is not a good default device :)
 249                         }
 250                     }
 251                 } catch (IllegalArgumentException e) {
 252                 } catch (NullPointerException e) {
 253                 }
 254             }
 255         }
 256 
 257 
 258         throw new IllegalArgumentException("Mixer not supported: "
 259                                            + (info!=null?info.toString():"null"));
 260     }
 261 
 262 
 263     //$$fb 2002-11-26: fix for 4757930: DOC: AudioSystem.getTarget/SourceLineInfo() is ambiguous

 264     /**
 265      * Obtains information about all source lines of a particular type that are supported
 266      * by the installed mixers.
 267      * @param info a <code>Line.Info</code> object that specifies the kind of
 268      * lines about which information is requested
 269      * @return an array of <code>Line.Info</code> objects describing source lines matching
 270      * the type requested.  If no matching source lines are supported, an array of length 0
 271      * is returned.
 272      *





 273      * @see Mixer#getSourceLineInfo(Line.Info)
 274      */
 275     public static Line.Info[] getSourceLineInfo(Line.Info info) {
 276 
 277         Vector vector = new Vector();
 278         Line.Info[] currentInfoArray;
 279 
 280         Mixer mixer;
 281         Line.Info fullInfo = null;
 282         Mixer.Info[] infoArray = getMixerInfo();
 283 
 284         for (int i = 0; i < infoArray.length; i++) {
 285 
 286             mixer = getMixer(infoArray[i]);
 287 
 288             currentInfoArray = mixer.getSourceLineInfo(info);
 289             for (int j = 0; j < currentInfoArray.length; j++) {
 290                 vector.addElement(currentInfoArray[j]);
 291             }
 292         }
 293 
 294         Line.Info[] returnedArray = new Line.Info[vector.size()];
 295 
 296         for (int i = 0; i < returnedArray.length; i++) {
 297             returnedArray[i] = (Line.Info)vector.get(i);
 298         }
 299 
 300         return returnedArray;
 301     }
 302 
 303 
 304     /**
 305      * Obtains information about all target lines of a particular type that are supported
 306      * by the installed mixers.
 307      * @param info a <code>Line.Info</code> object that specifies the kind of
 308      * lines about which information is requested
 309      * @return an array of <code>Line.Info</code> objects describing target lines matching
 310      * the type requested.  If no matching target lines are supported, an array of length 0
 311      * is returned.
 312      *





 313      * @see Mixer#getTargetLineInfo(Line.Info)
 314      */
 315     public static Line.Info[] getTargetLineInfo(Line.Info info) {
 316 
 317         Vector vector = new Vector();
 318         Line.Info[] currentInfoArray;
 319 
 320         Mixer mixer;
 321         Line.Info fullInfo = null;
 322         Mixer.Info[] infoArray = getMixerInfo();
 323 
 324         for (int i = 0; i < infoArray.length; i++) {
 325 
 326             mixer = getMixer(infoArray[i]);
 327 
 328             currentInfoArray = mixer.getTargetLineInfo(info);
 329             for (int j = 0; j < currentInfoArray.length; j++) {
 330                 vector.addElement(currentInfoArray[j]);
 331             }
 332         }
 333 
 334         Line.Info[] returnedArray = new Line.Info[vector.size()];
 335 
 336         for (int i = 0; i < returnedArray.length; i++) {
 337             returnedArray[i] = (Line.Info)vector.get(i);
 338         }
 339 
 340         return returnedArray;
 341     }
 342 
 343 
 344     /**
 345      * Indicates whether the system supports any lines that match
 346      * the specified <code>Line.Info</code> object.  A line is supported if
 347      * any installed mixer supports it.
 348      * @param info a <code>Line.Info</code> object describing the line for which support is queried
 349      * @return <code>true</code> if at least one matching line is
 350      * supported, otherwise <code>false</code>
 351      *

 352      * @see Mixer#isLineSupported(Line.Info)
 353      */
 354     public static boolean isLineSupported(Line.Info info) {
 355 
 356         Mixer mixer;
 357         Mixer.Info[] infoArray = getMixerInfo();
 358 
 359         for (int i = 0; i < infoArray.length; i++) {
 360 
 361             if( infoArray[i] != null ) {
 362                 mixer = getMixer(infoArray[i]);
 363                 if (mixer.isLineSupported(info)) {
 364                     return true;
 365                 }
 366             }
 367         }
 368 
 369         return false;
 370     }
 371 
 372     /**
 373      * Obtains a line that matches the description in the specified
 374      * <code>Line.Info</code> object.
 375      *
 376      * <p>If a <code>DataLine</code> is requested, and <code>info</code>
 377      * is an instance of <code>DataLine.Info</code> specifying at least
 378      * one fully qualified audio format, the last one
 379      * will be used as the default format of the returned
 380      * <code>DataLine</code>.
 381      *
 382      * <p>If system properties
 383      * <code>javax.sound.sampled.Clip</code>,
 384      * <code>javax.sound.sampled.Port</code>,
 385      * <code>javax.sound.sampled.SourceDataLine</code> and
 386      * <code>javax.sound.sampled.TargetDataLine</code> are defined
 387      * or they are defined in the file &quot;sound.properties&quot;,
 388      * they are used to retrieve default lines.
 389      * For details, refer to the {@link AudioSystem class description}.
 390      *
 391      * If the respective property is not set, or the mixer
 392      * requested in the property is not installed or does not provide the
 393      * requested line, all installed mixers are queried for the
 394      * requested line type. A Line will be returned from the first mixer
 395      * providing the requested line type.
 396      *
 397      * @param info a <code>Line.Info</code> object describing the desired kind of line

 398      * @return a line of the requested kind
 399      *
 400      * @throws LineUnavailableException if a matching line
 401      * is not available due to resource restrictions
 402      * @throws SecurityException if a matching line
 403      * is not available due to security restrictions
 404      * @throws IllegalArgumentException if the system does not
 405      * support at least one line matching the specified
 406      * <code>Line.Info</code> object
 407      * through any installed mixer
 408      */
 409     public static Line getLine(Line.Info info) throws LineUnavailableException {
 410         LineUnavailableException lue = null;
 411         List providers = getMixerProviders();
 412 
 413 
 414         // 1: try from default mixer for this line class
 415         try {
 416             Mixer mixer = getDefaultMixer(providers, info);
 417             if (mixer != null && mixer.isLineSupported(info)) {
 418                 return mixer.getLine(info);
 419             }
 420         } catch (LineUnavailableException e) {
 421             lue = e;
 422         } catch (IllegalArgumentException iae) {
 423             // must not happen... but better to catch it here,
 424             // if plug-ins are badly written
 425         }
 426 
 427 


 462                     lue = e;
 463                 } catch (IllegalArgumentException iae) {
 464                     // must not happen... but better to catch it here,
 465                     // if plug-ins are badly written
 466                 }
 467             }
 468         }
 469 
 470         // if this line was supported but was not available, throw the last
 471         // LineUnavailableException we got (??).
 472         if (lue != null) {
 473             throw lue;
 474         }
 475 
 476         // otherwise, the requested line was not supported, so throw
 477         // an Illegal argument exception
 478         throw new IllegalArgumentException("No line matching " +
 479                                            info.toString() + " is supported.");
 480     }
 481 
 482 
 483     /**
 484      * Obtains a clip that can be used for playing back
 485      * an audio file or an audio stream. The returned clip
 486      * will be provided by the default system mixer, or,
 487      * if not possible, by any other mixer installed in the
 488      * system that supports a <code>Clip</code>
 489      * object.
 490      *
 491      * <p>The returned clip must be opened with the
 492      * <code>open(AudioFormat)</code> or
 493      * <code>open(AudioInputStream)</code> method.
 494      *
 495      * <p>This is a high-level method that uses <code>getMixer</code>
 496      * and <code>getLine</code> internally.
 497      *
 498      * <p>If the system property
 499      * <code>javax.sound.sampled.Clip</code>
 500      * is defined or it is defined in the file &quot;sound.properties&quot;,
 501      * it is used to retrieve the default clip.
 502      * For details, refer to the {@link AudioSystem class description}.
 503      *
 504      * @return the desired clip object
 505      *
 506      * @throws LineUnavailableException if a clip object
 507      * is not available due to resource restrictions
 508      * @throws SecurityException if a clip object
 509      * is not available due to security restrictions
 510      * @throws IllegalArgumentException if the system does not
 511      * support at least one clip instance through any installed mixer
 512      *
 513      * @see #getClip(Mixer.Info)
 514      * @since 1.5
 515      */
 516     public static Clip getClip() throws LineUnavailableException{
 517         AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
 518                                              AudioSystem.NOT_SPECIFIED,
 519                                              16, 2, 4,
 520                                              AudioSystem.NOT_SPECIFIED, true);
 521         DataLine.Info info = new DataLine.Info(Clip.class, format);
 522         return (Clip) AudioSystem.getLine(info);
 523     }
 524 
 525 
 526     /**
 527      * Obtains a clip from the specified mixer that can be
 528      * used for playing back an audio file or an audio stream.






 529      *
 530      * <p>The returned clip must be opened with the
 531      * <code>open(AudioFormat)</code> or
 532      * <code>open(AudioInputStream)</code> method.
 533      *
 534      * <p>This is a high-level method that uses <code>getMixer</code>
 535      * and <code>getLine</code> internally.
 536      *
 537      * @param mixerInfo a <code>Mixer.Info</code> object representing the
 538      * desired mixer, or <code>null</code> for the system default mixer
 539      * @return a clip object from the specified mixer
 540      *
 541      * @throws LineUnavailableException if a clip
 542      * is not available from this mixer due to resource restrictions
 543      * @throws SecurityException if a clip
 544      * is not available from this mixer due to security restrictions
 545      * @throws IllegalArgumentException if the system does not
 546      * support at least one clip through the specified mixer
 547      *
 548      * @see #getClip()
 549      * @since 1.5
 550      */
 551     public static Clip getClip(Mixer.Info mixerInfo) throws LineUnavailableException{
 552         AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
 553                                              AudioSystem.NOT_SPECIFIED,
 554                                              16, 2, 4,
 555                                              AudioSystem.NOT_SPECIFIED, true);
 556         DataLine.Info info = new DataLine.Info(Clip.class, format);
 557         Mixer mixer = AudioSystem.getMixer(mixerInfo);
 558         return (Clip) mixer.getLine(info);
 559     }
 560 
 561 
 562     /**
 563      * Obtains a source data line that can be used for playing back
 564      * audio data in the format specified by the
 565      * <code>AudioFormat</code> object. The returned line
 566      * will be provided by the default system mixer, or,
 567      * if not possible, by any other mixer installed in the
 568      * system that supports a matching
 569      * <code>SourceDataLine</code> object.
 570      *
 571      * <p>The returned line should be opened with the
 572      * <code>open(AudioFormat)</code> or
 573      * <code>open(AudioFormat, int)</code> method.
 574      *
 575      * <p>This is a high-level method that uses <code>getMixer</code>
 576      * and <code>getLine</code> internally.
 577      *
 578      * <p>The returned <code>SourceDataLine</code>'s default
 579      * audio format will be initialized with <code>format</code>.
 580      *
 581      * <p>If the system property
 582      * <code>javax.sound.sampled.SourceDataLine</code>
 583      * is defined or it is defined in the file &quot;sound.properties&quot;,
 584      * it is used to retrieve the default source data line.
 585      * For details, refer to the {@link AudioSystem class description}.
 586      *
 587      * @param format an <code>AudioFormat</code> object specifying
 588      *        the supported audio format of the returned line,
 589      *        or <code>null</code> for any audio format
 590      * @return the desired <code>SourceDataLine</code> object
 591      *
 592      * @throws LineUnavailableException if a matching source data line
 593      *         is not available due to resource restrictions
 594      * @throws SecurityException if a matching source data line
 595      *         is not available due to security restrictions
 596      * @throws IllegalArgumentException if the system does not
 597      *         support at least one source data line supporting the
 598      *         specified audio format through any installed mixer
 599      *
 600      * @see #getSourceDataLine(AudioFormat, Mixer.Info)
 601      * @since 1.5
 602      */
 603     public static SourceDataLine getSourceDataLine(AudioFormat format)
 604         throws LineUnavailableException{
 605         DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
 606         return (SourceDataLine) AudioSystem.getLine(info);
 607     }
 608 
 609 
 610     /**
 611      * Obtains a source data line that can be used for playing back
 612      * audio data in the format specified by the
 613      * <code>AudioFormat</code> object, provided by the mixer
 614      * specified by the <code>Mixer.Info</code> object.
 615      *
 616      * <p>The returned line should be opened with the
 617      * <code>open(AudioFormat)</code> or
 618      * <code>open(AudioFormat, int)</code> method.
 619      *
 620      * <p>This is a high-level method that uses <code>getMixer</code>
 621      * and <code>getLine</code> internally.
 622      *
 623      * <p>The returned <code>SourceDataLine</code>'s default
 624      * audio format will be initialized with <code>format</code>.
 625      *
 626      * @param format an <code>AudioFormat</code> object specifying
 627      *        the supported audio format of the returned line,
 628      *        or <code>null</code> for any audio format
 629      * @param mixerinfo a <code>Mixer.Info</code> object representing
 630      *        the desired mixer, or <code>null</code> for the system
 631      *        default mixer
 632      * @return the desired <code>SourceDataLine</code> object
 633      *
 634      * @throws LineUnavailableException if a matching source data
 635      *         line is not available from the specified mixer due
 636      *         to resource restrictions
 637      * @throws SecurityException if a matching source data line
 638      *         is not available from the specified mixer due to
 639      *         security restrictions
 640      * @throws IllegalArgumentException if the specified mixer does
 641      *         not support at least one source data line supporting
 642      *         the specified audio format
 643      *
 644      * @see #getSourceDataLine(AudioFormat)
 645      * @since 1.5
 646      */
 647     public static SourceDataLine getSourceDataLine(AudioFormat format,
 648                                                    Mixer.Info mixerinfo)
 649         throws LineUnavailableException{
 650         DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
 651         Mixer mixer = AudioSystem.getMixer(mixerinfo);
 652         return (SourceDataLine) mixer.getLine(info);
 653   }
 654 
 655 
 656     /**
 657      * Obtains a target data line that can be used for recording
 658      * audio data in the format specified by the
 659      * <code>AudioFormat</code> object. The returned line
 660      * will be provided by the default system mixer, or,
 661      * if not possible, by any other mixer installed in the
 662      * system that supports a matching
 663      * <code>TargetDataLine</code> object.
 664      *
 665      * <p>The returned line should be opened with the
 666      * <code>open(AudioFormat)</code> or
 667      * <code>open(AudioFormat, int)</code> method.
 668      *
 669      * <p>This is a high-level method that uses <code>getMixer</code>
 670      * and <code>getLine</code> internally.
 671      *
 672      * <p>The returned <code>TargetDataLine</code>'s default
 673      * audio format will be initialized with <code>format</code>.
 674      *
 675      * <p>If the system property
 676      * {@code javax.sound.sampled.TargetDataLine}
 677      * is defined or it is defined in the file &quot;sound.properties&quot;,
 678      * it is used to retrieve the default target data line.
 679      * For details, refer to the {@link AudioSystem class description}.
 680      *
 681      * @param format an <code>AudioFormat</code> object specifying
 682      *        the supported audio format of the returned line,
 683      *        or <code>null</code> for any audio format
 684      * @return the desired <code>TargetDataLine</code> object
 685      *
 686      * @throws LineUnavailableException if a matching target data line
 687      *         is not available due to resource restrictions
 688      * @throws SecurityException if a matching target data line
 689      *         is not available due to security restrictions
 690      * @throws IllegalArgumentException if the system does not
 691      *         support at least one target data line supporting the
 692      *         specified audio format through any installed mixer
 693      *
 694      * @see #getTargetDataLine(AudioFormat, Mixer.Info)
 695      * @see AudioPermission
 696      * @since 1.5
 697      */
 698     public static TargetDataLine getTargetDataLine(AudioFormat format)
 699         throws LineUnavailableException{
 700 
 701         DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
 702         return (TargetDataLine) AudioSystem.getLine(info);
 703     }
 704 
 705 
 706 
 707     /**
 708      * Obtains a target data line that can be used for recording
 709      * audio data in the format specified by the
 710      * <code>AudioFormat</code> object, provided by the mixer
 711      * specified by the <code>Mixer.Info</code> object.
 712      *
 713      * <p>The returned line should be opened with the
 714      * <code>open(AudioFormat)</code> or
 715      * <code>open(AudioFormat, int)</code> method.
 716      *
 717      * <p>This is a high-level method that uses <code>getMixer</code>
 718      * and <code>getLine</code> internally.
 719      *
 720      * <p>The returned <code>TargetDataLine</code>'s default
 721      * audio format will be initialized with <code>format</code>.
 722      *
 723      * @param format an <code>AudioFormat</code> object specifying
 724      *        the supported audio format of the returned line,
 725      *        or <code>null</code> for any audio format
 726      * @param mixerinfo a <code>Mixer.Info</code> object representing the
 727      *        desired mixer, or <code>null</code> for the system default mixer
 728      * @return the desired <code>TargetDataLine</code> object
 729      *
 730      * @throws LineUnavailableException if a matching target data
 731      *         line is not available from the specified mixer due
 732      *         to resource restrictions
 733      * @throws SecurityException if a matching target data line
 734      *         is not available from the specified mixer due to
 735      *         security restrictions
 736      * @throws IllegalArgumentException if the specified mixer does
 737      *         not support at least one target data line supporting
 738      *         the specified audio format
 739      *
 740      * @see #getTargetDataLine(AudioFormat)
 741      * @see AudioPermission
 742      * @since 1.5
 743      */
 744     public static TargetDataLine getTargetDataLine(AudioFormat format,
 745                                                    Mixer.Info mixerinfo)
 746         throws LineUnavailableException {
 747 
 748         DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
 749         Mixer mixer = AudioSystem.getMixer(mixerinfo);
 750         return (TargetDataLine) mixer.getLine(info);
 751     }
 752 
 753 
 754     // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec

 755     /**
 756      * Obtains the encodings that the system can obtain from an
 757      * audio input stream with the specified encoding using the set
 758      * of installed format converters.
 759      * @param sourceEncoding the encoding for which conversion support
 760      * is queried
 761      * @return array of encodings.  If <code>sourceEncoding</code>is not supported,
 762      * an array of length 0 is returned. Otherwise, the array will have a length
 763      * of at least 1, representing <code>sourceEncoding</code> (no conversion).


 764      */
 765     public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding sourceEncoding) {
 766 
 767         List codecs = getFormatConversionProviders();
 768         Vector encodings = new Vector();
 769 
 770         AudioFormat.Encoding encs[] = null;
 771 
 772         // gather from all the codecs
 773         for(int i=0; i<codecs.size(); i++ ) {
 774             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 775             if( codec.isSourceEncodingSupported( sourceEncoding ) ) {
 776                 encs = codec.getTargetEncodings();
 777                 for (int j = 0; j < encs.length; j++) {
 778                     encodings.addElement( encs[j] );
 779                 }
 780             }
 781         }
 782         AudioFormat.Encoding encs2[] = (AudioFormat.Encoding[]) encodings.toArray(new AudioFormat.Encoding[0]);
 783         return encs2;
 784     }
 785 
 786 
 787 
 788     // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec

 789     /**
 790      * Obtains the encodings that the system can obtain from an
 791      * audio input stream with the specified format using the set
 792      * of installed format converters.
 793      * @param sourceFormat the audio format for which conversion
 794      * is queried
 795      * @return array of encodings. If <code>sourceFormat</code>is not supported,
 796      * an array of length 0 is returned. Otherwise, the array will have a length
 797      * of at least 1, representing the encoding of <code>sourceFormat</code> (no conversion).

 798      */
 799     public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) {
 800 
 801 
 802         List codecs = getFormatConversionProviders();
 803         Vector encodings = new Vector();
 804 
 805         int size = 0;
 806         int index = 0;
 807         AudioFormat.Encoding encs[] = null;
 808 
 809         // gather from all the codecs
 810 
 811         for(int i=0; i<codecs.size(); i++ ) {
 812             encs = ((FormatConversionProvider) codecs.get(i)).getTargetEncodings(sourceFormat);
 813             size += encs.length;
 814             encodings.addElement( encs );
 815         }
 816 
 817         // now build a new array
 818 
 819         AudioFormat.Encoding encs2[] = new AudioFormat.Encoding[size];
 820         for(int i=0; i<encodings.size(); i++ ) {
 821             encs = (AudioFormat.Encoding [])(encodings.get(i));
 822             for(int j=0; j<encs.length; j++ ) {
 823                 encs2[index++] = encs[j];
 824             }
 825         }
 826         return encs2;
 827     }
 828 
 829 
 830     /**
 831      * Indicates whether an audio input stream of the specified encoding
 832      * can be obtained from an audio input stream that has the specified
 833      * format.
 834      * @param targetEncoding the desired encoding after conversion
 835      * @param sourceFormat the audio format before conversion
 836      * @return <code>true</code> if the conversion is supported,
 837      * otherwise <code>false</code>
 838      */
 839     public static boolean isConversionSupported(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {
 840 
 841 
 842         List codecs = getFormatConversionProviders();
 843 
 844         for(int i=0; i<codecs.size(); i++ ) {
 845             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 846             if(codec.isConversionSupported(targetEncoding,sourceFormat) ) {
 847                 return true;
 848             }
 849         }
 850         return false;
 851     }
 852 
 853 
 854     /**
 855      * Obtains an audio input stream of the indicated encoding, by converting the
 856      * provided audio input stream.

 857      * @param targetEncoding the desired encoding after conversion
 858      * @param sourceStream the stream to be converted
 859      * @return an audio input stream of the indicated encoding
 860      * @throws IllegalArgumentException if the conversion is not supported
 861      * @see #getTargetEncodings(AudioFormat.Encoding)
 862      * @see #getTargetEncodings(AudioFormat)
 863      * @see #isConversionSupported(AudioFormat.Encoding, AudioFormat)
 864      * @see #getAudioInputStream(AudioFormat, AudioInputStream)
 865      */
 866     public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding,
 867                                                        AudioInputStream sourceStream) {
 868 
 869         List codecs = getFormatConversionProviders();
 870 
 871         for(int i = 0; i < codecs.size(); i++) {
 872             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 873             if( codec.isConversionSupported( targetEncoding, sourceStream.getFormat() ) ) {
 874                 return codec.getAudioInputStream( targetEncoding, sourceStream );
 875             }
 876         }
 877         // we ran out of options, throw an exception
 878         throw new IllegalArgumentException("Unsupported conversion: " + targetEncoding + " from " + sourceStream.getFormat());
 879     }
 880 
 881 
 882     /**
 883      * Obtains the formats that have a particular encoding and that the system can
 884      * obtain from a stream of the specified format using the set of
 885      * installed format converters.

 886      * @param targetEncoding the desired encoding after conversion
 887      * @param sourceFormat the audio format before conversion
 888      * @return array of formats.  If no formats of the specified
 889      * encoding are supported, an array of length 0 is returned.
 890      */
 891     public static AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {
 892 
 893         List codecs = getFormatConversionProviders();
 894         Vector formats = new Vector();
 895 
 896         int size = 0;
 897         int index = 0;
 898         AudioFormat fmts[] = null;
 899 
 900         // gather from all the codecs
 901 
 902         for(int i=0; i<codecs.size(); i++ ) {
 903             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 904             fmts = codec.getTargetFormats(targetEncoding, sourceFormat);
 905             size += fmts.length;
 906             formats.addElement( fmts );
 907         }
 908 
 909         // now build a new array
 910 
 911         AudioFormat fmts2[] = new AudioFormat[size];
 912         for(int i=0; i<formats.size(); i++ ) {
 913             fmts = (AudioFormat [])(formats.get(i));
 914             for(int j=0; j<fmts.length; j++ ) {
 915                 fmts2[index++] = fmts[j];
 916             }
 917         }
 918         return fmts2;
 919     }
 920 
 921 
 922     /**
 923      * Indicates whether an audio input stream of a specified format
 924      * can be obtained from an audio input stream of another specified format.

 925      * @param targetFormat the desired audio format after conversion
 926      * @param sourceFormat the audio format before conversion
 927      * @return <code>true</code> if the conversion is supported,
 928      * otherwise <code>false</code>
 929      */
 930 
 931     public static boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) {
 932 
 933         List codecs = getFormatConversionProviders();
 934 
 935         for(int i=0; i<codecs.size(); i++ ) {
 936             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 937             if(codec.isConversionSupported(targetFormat, sourceFormat) ) {
 938                 return true;
 939             }
 940         }
 941         return false;
 942     }
 943 
 944 
 945     /**
 946      * Obtains an audio input stream of the indicated format, by converting the
 947      * provided audio input stream.

 948      * @param targetFormat the desired audio format after conversion
 949      * @param sourceStream the stream to be converted
 950      * @return an audio input stream of the indicated format
 951      * @throws IllegalArgumentException if the conversion is not supported
 952      * #see #getTargetEncodings(AudioFormat)
 953      * @see #getTargetFormats(AudioFormat.Encoding, AudioFormat)
 954      * @see #isConversionSupported(AudioFormat, AudioFormat)
 955      * @see #getAudioInputStream(AudioFormat.Encoding, AudioInputStream)
 956      */
 957     public static AudioInputStream getAudioInputStream(AudioFormat targetFormat,
 958                                                        AudioInputStream sourceStream) {
 959 
 960         if (sourceStream.getFormat().matches(targetFormat)) {
 961             return sourceStream;
 962         }
 963 
 964         List codecs = getFormatConversionProviders();
 965 
 966         for(int i = 0; i < codecs.size(); i++) {
 967             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 968             if(codec.isConversionSupported(targetFormat,sourceStream.getFormat()) ) {
 969                 return codec.getAudioInputStream(targetFormat,sourceStream);
 970             }
 971         }
 972 
 973         // we ran out of options...
 974         throw new IllegalArgumentException("Unsupported conversion: " + targetFormat + " from " + sourceStream.getFormat());
 975     }
 976 
 977 
 978     /**
 979      * Obtains the audio file format of the provided input stream.  The stream must
 980      * point to valid audio file data.  The implementation of this method may require
 981      * multiple parsers to examine the stream to determine whether they support it.
 982      * These parsers must be able to mark the stream, read enough data to determine whether they
 983      * support the stream, and, if not, reset the stream's read pointer to its original
 984      * position.  If the input stream does not support these operations, this method may fail
 985      * with an <code>IOException</code>.
 986      * @param stream the input stream from which file format information should be
 987      * extracted
 988      * @return an <code>AudioFileFormat</code> object describing the stream's audio file format
 989      * @throws UnsupportedAudioFileException if the stream does not point to valid audio
 990      * file data recognized by the system



 991      * @throws IOException if an input/output exception occurs
 992      * @see InputStream#markSupported
 993      * @see InputStream#mark
 994      */
 995     public static AudioFileFormat getAudioFileFormat(InputStream stream)
 996         throws UnsupportedAudioFileException, IOException {
 997 
 998         List providers = getAudioFileReaders();
 999         AudioFileFormat format = null;
1000 
1001         for(int i = 0; i < providers.size(); i++ ) {
1002             AudioFileReader reader = (AudioFileReader) providers.get(i);
1003             try {
1004                 format = reader.getAudioFileFormat( stream ); // throws IOException
1005                 break;
1006             } catch (UnsupportedAudioFileException e) {
1007                 continue;
1008             }
1009         }
1010 
1011         if( format==null ) {
1012             throw new UnsupportedAudioFileException("file is not a supported file type");
1013         } else {
1014             return format;
1015         }
1016     }
1017 
1018     /**
1019      * Obtains the audio file format of the specified URL.  The URL must
1020      * point to valid audio file data.

1021      * @param url the URL from which file format information should be
1022      * extracted
1023      * @return an <code>AudioFileFormat</code> object describing the audio file format
1024      * @throws UnsupportedAudioFileException if the URL does not point to valid audio
1025      * file data recognized by the system

1026      * @throws IOException if an input/output exception occurs
1027      */
1028     public static AudioFileFormat getAudioFileFormat(URL url)
1029         throws UnsupportedAudioFileException, IOException {
1030 
1031         List providers = getAudioFileReaders();
1032         AudioFileFormat format = null;
1033 
1034         for(int i = 0; i < providers.size(); i++ ) {
1035             AudioFileReader reader = (AudioFileReader) providers.get(i);
1036             try {
1037                 format = reader.getAudioFileFormat( url ); // throws IOException
1038                 break;
1039             } catch (UnsupportedAudioFileException e) {
1040                 continue;
1041             }
1042         }
1043 
1044         if( format==null ) {
1045             throw new UnsupportedAudioFileException("file is not a supported file type");
1046         } else {
1047             return format;
1048         }
1049     }
1050 
1051     /**
1052      * Obtains the audio file format of the specified <code>File</code>.  The <code>File</code> must
1053      * point to valid audio file data.
1054      * @param file the <code>File</code> from which file format information should be
1055      * extracted
1056      * @return an <code>AudioFileFormat</code> object describing the audio file format
1057      * @throws UnsupportedAudioFileException if the <code>File</code> does not point to valid audio
1058      * file data recognized by the system


1059      * @throws IOException if an I/O exception occurs
1060      */
1061     public static AudioFileFormat getAudioFileFormat(File file)
1062         throws UnsupportedAudioFileException, IOException {
1063 
1064         List providers = getAudioFileReaders();
1065         AudioFileFormat format = null;
1066 
1067         for(int i = 0; i < providers.size(); i++ ) {
1068             AudioFileReader reader = (AudioFileReader) providers.get(i);
1069             try {
1070                 format = reader.getAudioFileFormat( file ); // throws IOException
1071                 break;
1072             } catch (UnsupportedAudioFileException e) {
1073                 continue;
1074             }
1075         }
1076 
1077         if( format==null ) {
1078             throw new UnsupportedAudioFileException("file is not a supported file type");
1079         } else {
1080             return format;
1081         }
1082     }
1083 
1084 
1085     /**
1086      * Obtains an audio input stream from the provided input stream.  The stream must
1087      * point to valid audio file data.  The implementation of this method may
1088      * require multiple parsers to
1089      * examine the stream to determine whether they support it.  These parsers must
1090      * be able to mark the stream, read enough data to determine whether they
1091      * support the stream, and, if not, reset the stream's read pointer to its original
1092      * position.  If the input stream does not support these operation, this method may fail
1093      * with an <code>IOException</code>.
1094      * @param stream the input stream from which the <code>AudioInputStream</code> should be
1095      * constructed
1096      * @return an <code>AudioInputStream</code> object based on the audio file data contained
1097      * in the input stream.
1098      * @throws UnsupportedAudioFileException if the stream does not point to valid audio
1099      * file data recognized by the system

1100      * @throws IOException if an I/O exception occurs
1101      * @see InputStream#markSupported
1102      * @see InputStream#mark
1103      */
1104     public static AudioInputStream getAudioInputStream(InputStream stream)
1105         throws UnsupportedAudioFileException, IOException {
1106 
1107         List providers = getAudioFileReaders();
1108         AudioInputStream audioStream = null;
1109 
1110         for(int i = 0; i < providers.size(); i++ ) {
1111             AudioFileReader reader = (AudioFileReader) providers.get(i);
1112             try {
1113                 audioStream = reader.getAudioInputStream( stream ); // throws IOException
1114                 break;
1115             } catch (UnsupportedAudioFileException e) {
1116                 continue;
1117             }
1118         }
1119 
1120         if( audioStream==null ) {
1121             throw new UnsupportedAudioFileException("could not get audio input stream from input stream");
1122         } else {
1123             return audioStream;
1124         }
1125     }
1126 
1127     /**
1128      * Obtains an audio input stream from the URL provided.  The URL must
1129      * point to valid audio file data.
1130      * @param url the URL for which the <code>AudioInputStream</code> should be

1131      * constructed
1132      * @return an <code>AudioInputStream</code> object based on the audio file data pointed
1133      * to by the URL
1134      * @throws UnsupportedAudioFileException if the URL does not point to valid audio
1135      * file data recognized by the system
1136      * @throws IOException if an I/O exception occurs
1137      */
1138     public static AudioInputStream getAudioInputStream(URL url)
1139         throws UnsupportedAudioFileException, IOException {
1140 
1141         List providers = getAudioFileReaders();
1142         AudioInputStream audioStream = null;
1143 
1144         for(int i = 0; i < providers.size(); i++ ) {
1145             AudioFileReader reader = (AudioFileReader) providers.get(i);
1146             try {
1147                 audioStream = reader.getAudioInputStream( url ); // throws IOException
1148                 break;
1149             } catch (UnsupportedAudioFileException e) {
1150                 continue;
1151             }
1152         }
1153 
1154         if( audioStream==null ) {
1155             throw new UnsupportedAudioFileException("could not get audio input stream from input URL");
1156         } else {
1157             return audioStream;
1158         }
1159     }
1160 
1161     /**
1162      * Obtains an audio input stream from the provided <code>File</code>.  The <code>File</code> must
1163      * point to valid audio file data.
1164      * @param file the <code>File</code> for which the <code>AudioInputStream</code> should be
1165      * constructed
1166      * @return an <code>AudioInputStream</code> object based on the audio file data pointed
1167      * to by the <code>File</code>
1168      * @throws UnsupportedAudioFileException if the <code>File</code> does not point to valid audio
1169      * file data recognized by the system

1170      * @throws IOException if an I/O exception occurs
1171      */
1172     public static AudioInputStream getAudioInputStream(File file)
1173         throws UnsupportedAudioFileException, IOException {
1174 
1175         List providers = getAudioFileReaders();
1176         AudioInputStream audioStream = null;
1177 
1178         for(int i = 0; i < providers.size(); i++ ) {
1179             AudioFileReader reader = (AudioFileReader) providers.get(i);
1180             try {
1181                 audioStream = reader.getAudioInputStream( file ); // throws IOException
1182                 break;
1183             } catch (UnsupportedAudioFileException e) {
1184                 continue;
1185             }
1186         }
1187 
1188         if( audioStream==null ) {
1189             throw new UnsupportedAudioFileException("could not get audio input stream from input file");
1190         } else {
1191             return audioStream;
1192         }
1193     }
1194 
1195 
1196     /**
1197      * Obtains the file types for which file writing support is provided by the system.
1198      * @return array of unique file types.  If no file types are supported,
1199      * an array of length 0 is returned.


1200      */
1201     public static AudioFileFormat.Type[] getAudioFileTypes() {
1202         List providers = getAudioFileWriters();
1203         Set returnTypesSet = new HashSet();
1204 
1205         for(int i=0; i < providers.size(); i++) {
1206             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1207             AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes();
1208             for(int j=0; j < fileTypes.length; j++) {
1209                 returnTypesSet.add(fileTypes[j]);
1210             }
1211         }
1212         AudioFileFormat.Type returnTypes[] = (AudioFileFormat.Type[])
1213             returnTypesSet.toArray(new AudioFileFormat.Type[0]);
1214         return returnTypes;
1215     }
1216 
1217 
1218     /**
1219      * Indicates whether file writing support for the specified file type is provided
1220      * by the system.

1221      * @param fileType the file type for which write capabilities are queried
1222      * @return <code>true</code> if the file type is supported,
1223      * otherwise <code>false</code>
1224      */
1225     public static boolean isFileTypeSupported(AudioFileFormat.Type fileType) {
1226 
1227         List providers = getAudioFileWriters();
1228 
1229         for(int i=0; i < providers.size(); i++) {
1230             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1231             if (writer.isFileTypeSupported(fileType)) {
1232                 return true;
1233             }
1234         }
1235         return false;
1236     }
1237 
1238 
1239     /**
1240      * Obtains the file types that the system can write from the
1241      * audio input stream specified.
1242      * @param stream the audio input stream for which audio file type support
1243      * is queried
1244      * @return array of file types.  If no file types are supported,
1245      * an array of length 0 is returned.

1246      */
1247     public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
1248         List providers = getAudioFileWriters();
1249         Set returnTypesSet = new HashSet();
1250 
1251         for(int i=0; i < providers.size(); i++) {
1252             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1253             AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes(stream);
1254             for(int j=0; j < fileTypes.length; j++) {
1255                 returnTypesSet.add(fileTypes[j]);
1256             }
1257         }
1258         AudioFileFormat.Type returnTypes[] = (AudioFileFormat.Type[])
1259             returnTypesSet.toArray(new AudioFileFormat.Type[0]);
1260         return returnTypes;
1261     }
1262 
1263 
1264     /**
1265      * Indicates whether an audio file of the specified file type can be written
1266      * from the indicated audio input stream.

1267      * @param fileType the file type for which write capabilities are queried
1268      * @param stream the stream for which file-writing support is queried
1269      * @return <code>true</code> if the file type is supported for this audio input stream,
1270      * otherwise <code>false</code>
1271      */
1272     public static boolean isFileTypeSupported(AudioFileFormat.Type fileType,
1273                                               AudioInputStream stream) {
1274 
1275         List providers = getAudioFileWriters();
1276 
1277         for(int i=0; i < providers.size(); i++) {
1278             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1279             if(writer.isFileTypeSupported(fileType, stream)) {
1280                 return true;
1281             }
1282         }
1283         return false;
1284     }
1285 
1286 
1287     /**
1288      * Writes a stream of bytes representing an audio file of the specified file type
1289      * to the output stream provided.  Some file types require that
1290      * the length be written into the file header; such files cannot be written from
1291      * start to finish unless the length is known in advance.  An attempt
1292      * to write a file of such a type will fail with an IOException if the length in
1293      * the audio file type is <code>AudioSystem.NOT_SPECIFIED</code>.
1294      *
1295      * @param stream the audio input stream containing audio data to be
1296      * written to the file
1297      * @param fileType the kind of audio file to write
1298      * @param out the stream to which the file data should be written
1299      * @return the number of bytes written to the output stream
1300      * @throws IOException if an input/output exception occurs
1301      * @throws IllegalArgumentException if the file type is not supported by
1302      * the system
1303      * @see #isFileTypeSupported
1304      * @see     #getAudioFileTypes
1305      */
1306     public static int write(AudioInputStream stream, AudioFileFormat.Type fileType,
1307                             OutputStream out) throws IOException {
1308 
1309         List providers = getAudioFileWriters();
1310         int bytesWritten = 0;
1311         boolean flag = false;
1312 
1313         for(int i=0; i < providers.size(); i++) {
1314             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1315             try {
1316                 bytesWritten = writer.write( stream, fileType, out ); // throws IOException
1317                 flag = true;
1318                 break;
1319             } catch (IllegalArgumentException e) {
1320                 // thrown if this provider cannot write the sequence, try the next
1321                 continue;
1322             }
1323         }
1324         if(!flag) {
1325             throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType);
1326         } else {
1327             return bytesWritten;
1328         }
1329     }
1330 
1331 
1332     /**
1333      * Writes a stream of bytes representing an audio file of the specified file type
1334      * to the external file provided.
1335      * @param stream the audio input stream containing audio data to be
1336      * written to the file

1337      * @param fileType the kind of audio file to write
1338      * @param out the external file to which the file data should be written
1339      * @return the number of bytes written to the file
1340      * @throws IOException if an I/O exception occurs
1341      * @throws IllegalArgumentException if the file type is not supported by
1342      * the system
1343      * @see #isFileTypeSupported
1344      * @see     #getAudioFileTypes
1345      */
1346     public static int write(AudioInputStream stream, AudioFileFormat.Type fileType,
1347                             File out) throws IOException {
1348 
1349         List providers = getAudioFileWriters();
1350         int bytesWritten = 0;
1351         boolean flag = false;
1352 
1353         for(int i=0; i < providers.size(); i++) {
1354             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1355             try {
1356                 bytesWritten = writer.write( stream, fileType, out ); // throws IOException
1357                 flag = true;
1358                 break;
1359             } catch (IllegalArgumentException e) {
1360                 // thrown if this provider cannot write the sequence, try the next
1361                 continue;
1362             }
1363         }
1364         if (!flag) {
1365             throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType);
1366         } else {
1367             return bytesWritten;
1368         }
1369     }
1370 
1371 
1372     // METHODS FOR INTERNAL IMPLEMENTATION USE
1373 
1374     /**
1375      * Obtains the set of MixerProviders on the system.
1376      */
1377     private static List getMixerProviders() {
1378         return getProviders(MixerProvider.class);
1379     }
1380 
1381 
1382     /**
1383      * Obtains the set of format converters (codecs, transcoders, etc.)
1384      * that are currently installed on the system.
1385      * @return an array of
1386      * {@link javax.sound.sampled.spi.FormatConversionProvider
1387      * FormatConversionProvider}
1388      * objects representing the available format converters.  If no format
1389      * converters readers are available on the system, an array of length 0 is
1390      * returned.
1391      */
1392     private static List getFormatConversionProviders() {
1393         return getProviders(FormatConversionProvider.class);
1394     }
1395 
1396 
1397     /**
1398      * Obtains the set of audio file readers that are currently installed on the system.
1399      * @return a List of
1400      * {@link javax.sound.sampled.spi.AudioFileReader
1401      * AudioFileReader}
1402      * objects representing the installed audio file readers.  If no audio file
1403      * readers are available on the system, an empty List is returned.

1404      */
1405     private static List getAudioFileReaders() {
1406         return getProviders(AudioFileReader.class);
1407     }
1408 
1409 
1410     /**
1411      * Obtains the set of audio file writers that are currently installed on the system.
1412      * @return a List of
1413      * {@link javax.sound.samples.spi.AudioFileWriter AudioFileWriter}
1414      * objects representing the available audio file writers.  If no audio file
1415      * writers are available on the system, an empty List is returned.


1416      */
1417     private static List getAudioFileWriters() {
1418         return getProviders(AudioFileWriter.class);
1419     }
1420 
1421 
1422 
1423     /** Attempts to locate and return a default Mixer that provides lines
1424      * of the specified type.
1425      *
1426      * @param providers the installed mixer providers
1427      * @param info The requested line type
1428      * TargetDataLine.class, Clip.class or Port.class.
1429      * @return a Mixer that matches the requirements, or null if no default mixer found

1430      */
1431     private static Mixer getDefaultMixer(List providers, Line.Info info) {
1432         Class lineClass = info.getLineClass();
1433         String providerClassName = JDK13Services.getDefaultProviderClassName(lineClass);
1434         String instanceName = JDK13Services.getDefaultInstanceName(lineClass);
1435         Mixer mixer;
1436 
1437         if (providerClassName != null) {
1438             MixerProvider defaultProvider = getNamedProvider(providerClassName, providers);
1439             if (defaultProvider != null) {
1440                 if (instanceName != null) {
1441                     mixer = getNamedMixer(instanceName, defaultProvider, info);
1442                     if (mixer != null) {
1443                         return mixer;
1444                     }
1445                 } else {
1446                     mixer = getFirstMixer(defaultProvider, info,
1447                                           false /* mixing not required*/);
1448                     if (mixer != null) {
1449                         return mixer;


1452 
1453             }
1454         }
1455 
1456         /* Provider class not specified or
1457            provider class cannot be found, or
1458            provider class and instance specified and instance cannot be found or is not appropriate */
1459         if (instanceName != null) {
1460             mixer = getNamedMixer(instanceName, providers, info);
1461             if (mixer != null) {
1462                 return mixer;
1463             }
1464         }
1465 
1466 
1467         /* No default are specified, or if something is specified, everything
1468            failed. */
1469         return null;
1470     }
1471 
1472 
1473 
1474     /** Return a MixerProvider of a given class from the list of
1475         MixerProviders.
1476 
1477         This method never requires the returned Mixer to do mixing.
1478         @param providerClassName The class name of the provider to be returned.
1479         @param providers The list of MixerProviders that is searched.
1480         @return A MixerProvider of the requested class, or null if none is
1481         found.
1482      */
1483     private static MixerProvider getNamedProvider(String providerClassName,
1484                                                   List providers) {
1485         for(int i = 0; i < providers.size(); i++) {
1486             MixerProvider provider = (MixerProvider) providers.get(i);
1487             if (provider.getClass().getName().equals(providerClassName)) {
1488                 return provider;
1489             }
1490         }
1491         return null;
1492     }
1493 
1494 
1495     /** Return a Mixer with a given name from a given MixerProvider.
1496       This method never requires the returned Mixer to do mixing.
1497       @param mixerName The name of the Mixer to be returned.
1498       @param provider The MixerProvider to check for Mixers.
1499       @param info The type of line the returned Mixer is required to
1500       support.
1501 
1502       @return A Mixer matching the requirements, or null if none is found.
1503      */
1504     private static Mixer getNamedMixer(String mixerName,
1505                                        MixerProvider provider,
1506                                        Line.Info info) {
1507         Mixer.Info[] infos = provider.getMixerInfo();
1508         for (int i = 0; i < infos.length; i++) {
1509             if (infos[i].getName().equals(mixerName)) {
1510                 Mixer mixer = provider.getMixer(infos[i]);
1511                 if (isAppropriateMixer(mixer, info, false)) {
1512                     return mixer;
1513                 }
1514             }
1515         }
1516         return null;
1517     }
1518 
1519 
1520     /** From a List of MixerProviders, return a Mixer with a given name.
1521         This method never requires the returned Mixer to do mixing.
1522         @param mixerName The name of the Mixer to be returned.
1523         @param providers The List of MixerProviders to check for Mixers.
1524         @param info The type of line the returned Mixer is required to
1525         support.
1526         @return A Mixer matching the requirements, or null if none is found.
1527      */
1528     private static Mixer getNamedMixer(String mixerName,
1529                                        List providers,
1530                                        Line.Info info) {
1531         for(int i = 0; i < providers.size(); i++) {
1532             MixerProvider provider = (MixerProvider) providers.get(i);
1533             Mixer mixer = getNamedMixer(mixerName, provider, info);
1534             if (mixer != null) {
1535                 return mixer;
1536             }
1537         }
1538         return null;
1539     }
1540 
1541 
1542     /** From a given MixerProvider, return the first appropriate Mixer.
1543         @param provider The MixerProvider to check for Mixers.
1544         @param info The type of line the returned Mixer is required to
1545         support.
1546         @param isMixingRequired If true, only Mixers that support mixing are
1547         returned for line types of SourceDataLine and Clip.
1548 
1549         @return A Mixer that is considered appropriate, or null
1550         if none is found.
1551      */
1552     private static Mixer getFirstMixer(MixerProvider provider,
1553                                        Line.Info info,
1554                                        boolean isMixingRequired) {
1555         Mixer.Info[] infos = provider.getMixerInfo();
1556         for (int j = 0; j < infos.length; j++) {
1557             Mixer mixer = provider.getMixer(infos[j]);
1558             if (isAppropriateMixer(mixer, info, isMixingRequired)) {
1559                 return mixer;
1560             }
1561         }
1562         return null;
1563     }
1564 
1565 
1566     /** Checks if a Mixer is appropriate.
1567         A Mixer is considered appropriate if it support the given line type.
1568         If isMixingRequired is true and the line type is an output one
1569         (SourceDataLine, Clip), the mixer is appropriate if it supports
1570         at least 2 (concurrent) lines of the given type.
1571 
1572         @return true if the mixer is considered appropriate according to the
1573         rules given above, false otherwise.
1574      */
1575     private static boolean isAppropriateMixer(Mixer mixer,
1576                                               Line.Info lineInfo,
1577                                               boolean isMixingRequired) {
1578         if (! mixer.isLineSupported(lineInfo)) {
1579             return false;
1580         }
1581         Class lineClass = lineInfo.getLineClass();
1582         if (isMixingRequired
1583             && (SourceDataLine.class.isAssignableFrom(lineClass) ||
1584                 Clip.class.isAssignableFrom(lineClass))) {
1585             int maxLines = mixer.getMaxLines(lineInfo);
1586             return ((maxLines == NOT_SPECIFIED) || (maxLines > 1));
1587         }
1588         return true;
1589     }
1590 
1591 
1592 
1593     /**
1594      * Like getMixerInfo, but return List
1595      */
1596     private static List getMixerInfoList() {
1597         List providers = getMixerProviders();
1598         return getMixerInfoList(providers);
1599     }
1600 
1601 
1602     /**
1603      * Like getMixerInfo, but return List
1604      */
1605     private static List getMixerInfoList(List providers) {
1606         List infos = new ArrayList();
1607 
1608         Mixer.Info[] someInfos; // per-mixer
1609         Mixer.Info[] allInfos;  // for all mixers
1610 
1611         for(int i = 0; i < providers.size(); i++ ) {
1612             someInfos = ((MixerProvider)providers.get(i)).getMixerInfo();
1613 
1614             for (int j = 0; j < someInfos.length; j++) {
1615                 infos.add(someInfos[j]);
1616             }
1617         }
1618 
1619         return infos;
1620     }
1621 
1622 
1623     /**
1624      * Obtains the set of services currently installed on the system
1625      * using the SPI mechanism in 1.3.
1626      * @return a List of instances of providers for the requested service.
1627      * If no providers are available, a vector of length 0 will be returned.

1628      */
1629     private static List getProviders(Class providerClass) {
1630         return JDK13Services.getProviders(providerClass);
1631     }
1632 }


  33 
  34 import java.util.HashSet;
  35 import java.util.List;
  36 import java.util.Set;
  37 import java.util.Vector;
  38 import java.util.ArrayList;
  39 
  40 import javax.sound.sampled.spi.AudioFileWriter;
  41 import javax.sound.sampled.spi.AudioFileReader;
  42 import javax.sound.sampled.spi.FormatConversionProvider;
  43 import javax.sound.sampled.spi.MixerProvider;
  44 
  45 import com.sun.media.sound.JDK13Services;
  46 
  47 /* $fb TODO:
  48  * - consistent usage of (typed) collections
  49  */
  50 
  51 
  52 /**
  53  * The {@code AudioSystem} class acts as the entry point to the sampled-audio
  54  * system resources. This class lets you query and access the mixers that are
  55  * installed on the system. {@code AudioSystem} includes a number of methods for
  56  * converting audio data between different formats, and for translating between
  57  * audio files and streams. It also provides a method for obtaining a
  58  * {@link Line} directly from the {@code AudioSystem} without dealing explicitly


  59  * with mixers.
  60  * <p>
  61  * Properties can be used to specify the default mixer for specific line types.
  62  * Both system properties and a properties file are considered. The
  63  * {@code sound.properties} properties file is read from an
  64  * implementation-specific location (typically it is the {@code lib} directory
  65  * in the Java installation directory). If a property exists both as a system
  66  * property and in the properties file, the system property takes precedence.
  67  * If none is specified, a suitable default is chosen among the available
  68  * devices. The syntax of the properties file is specified in
  69  * {@link java.util.Properties#load(InputStream) Properties.load}. The following
  70  * table lists the available property keys and which methods consider them:



  71  *
  72  * <table border=0>
  73  *  <caption>Audio System Property Keys</caption>
  74  *  <tr>
  75  *   <th>Property Key</th>
  76  *   <th>Interface</th>
  77  *   <th>Affected Method(s)</th>
  78  *  </tr>
  79  *  <tr>
  80  *   <td>{@code javax.sound.sampled.Clip}</td>
  81  *   <td>{@link Clip}</td>
  82  *   <td>{@link #getLine}, {@link #getClip}</td>
  83  *  </tr>
  84  *  <tr>
  85  *   <td>{@code javax.sound.sampled.Port}</td>
  86  *   <td>{@link Port}</td>
  87  *   <td>{@link #getLine}</td>
  88  *  </tr>
  89  *  <tr>
  90  *   <td>{@code javax.sound.sampled.SourceDataLine}</td>
  91  *   <td>{@link SourceDataLine}</td>
  92  *   <td>{@link #getLine}, {@link #getSourceDataLine}</td>
  93  *  </tr>
  94  *  <tr>
  95  *   <td>{@code javax.sound.sampled.TargetDataLine}</td>
  96  *   <td>{@link TargetDataLine}</td>
  97  *   <td>{@link #getLine}, {@link #getTargetDataLine}</td>
  98  *  </tr>
  99  * </table>
 100  *
 101  * The property value consists of the provider class name and the mixer name,
 102  * separated by the hash mark (&quot;#&quot;). The provider class name is the
 103  * fully-qualified name of a concrete
 104  * {@link javax.sound.sampled.spi.MixerProvider mixer provider} class. The mixer
 105  * name is matched against the {@code String} returned by the {@code getName}
 106  * method of {@code Mixer.Info}. Either the class name, or the mixer name may be
 107  * omitted. If only the class name is specified, the trailing hash mark is
 108  * optional.
 109  * <p>
 110  * If the provider class is specified, and it can be successfully retrieved from
 111  * the installed providers, the list of {@code Mixer.Info} objects is retrieved
 112  * from the provider. Otherwise, or when these mixers do not provide a
 113  * subsequent match, the list is retrieved from {@link #getMixerInfo} to contain
 114  * all available {@code Mixer.Info} objects.
 115  * <p>
 116  * If a mixer name is specified, the resulting list of {@code Mixer.Info}
 117  * objects is searched: the first one with a matching name, and whose
 118  * {@code Mixer} provides the respective line interface, will be returned. If no
 119  * matching {@code Mixer.Info} object is found, or the mixer name is not
 120  * specified, the first mixer from the resulting list, which provides the



 121  * respective line interface, will be returned.
 122  *
 123  * For example, the property {@code javax.sound.sampled.Clip} with a value
 124  * {@code "com.sun.media.sound.MixerProvider#SunClip"}
 125  * will have the following consequences when {@code getLine} is called
 126  * requesting a {@code Clip} instance: if the class
 127  * {@code com.sun.media.sound.MixerProvider} exists in the list of installed
 128  * mixer providers, the first {@code Clip} from the first mixer with name
 129  * {@code "SunClip"} will be returned. If it cannot be found, the
 130  * first {@code Clip} from the first mixer of the specified provider will be
 131  * returned, regardless of name. If there is none, the first {@code Clip} from
 132  * the first {@code Mixer} with name {@code "SunClip"} in the list of
 133  * all mixers (as returned by {@code getMixerInfo}) will be returned, or, if not
 134  * found, the first {@code Clip} of the first {@code Mixer} that can be found in
 135  * the list of all mixers is returned. If that fails, too, an
 136  * {@code IllegalArgumentException} is thrown.












 137  *
 138  * @author Kara Kytle
 139  * @author Florian Bomers
 140  * @author Matthias Pfisterer
 141  * @author Kevin P. Smith

 142  * @see AudioFormat
 143  * @see AudioInputStream
 144  * @see Mixer
 145  * @see Line
 146  * @see Line.Info
 147  * @since 1.3
 148  */
 149 public class AudioSystem {
 150 
 151     /**
 152      * An integer that stands for an unknown numeric value. This value is
 153      * appropriate only for signed quantities that do not normally take negative
 154      * values. Examples include file sizes, frame sizes, buffer sizes, and
 155      * sample rates. A number of Java Sound constructors accept a value of
 156      * {@code NOT_SPECIFIED} for such parameters. Other methods may also accept
 157      * or return this value, as documented.

 158      */
 159     public static final int NOT_SPECIFIED = -1;
 160 
 161     /**
 162      * Private no-args constructor for ensuring against instantiation.
 163      */
 164     private AudioSystem() {
 165     }
 166 

 167     /**
 168      * Obtains an array of mixer info objects that represents the set of audio
 169      * mixers that are currently installed on the system.
 170      *
 171      * @return an array of info objects for the currently installed mixers. If
 172      *         no mixers are available on the system, an array of length 0 is
 173      *         returned.
 174      * @see #getMixer
 175      */
 176     public static Mixer.Info[] getMixerInfo() {
 177 
 178         List infos = getMixerInfoList();
 179         Mixer.Info[] allInfos = (Mixer.Info[]) infos.toArray(new Mixer.Info[infos.size()]);
 180         return allInfos;
 181     }
 182 

 183     /**
 184      * Obtains the requested audio mixer.
 185      *
 186      * @param  info a {@code Mixer.Info} object representing the desired mixer,
 187      *         or {@code null} for the system default mixer
 188      * @return the requested mixer
 189      * @throws SecurityException if the requested mixer is unavailable because
 190      *         of security restrictions
 191      * @throws IllegalArgumentException if the info object does not represent a
 192      *         mixer installed on the system
 193      * @see #getMixerInfo
 194      */
 195     public static Mixer getMixer(Mixer.Info info) {
 196 
 197         Mixer mixer = null;
 198         List providers = getMixerProviders();
 199 
 200         for(int i = 0; i < providers.size(); i++ ) {
 201 
 202             try {
 203                 return ((MixerProvider)providers.get(i)).getMixer(info);
 204 
 205             } catch (IllegalArgumentException e) {
 206             } catch (NullPointerException e) {
 207                 // $$jb 08.20.99:  If the strings in the info object aren't
 208                 // set, then Netscape (using jdk1.1.5) tends to throw
 209                 // NPE's when doing some string manipulation.  This is
 210                 // probably not the best fix, but is solves the problem
 211                 // of the NPE in Netscape using local classes
 212                 // $$jb 11.01.99: Replacing this patch.


 221                     Mixer.Info[] infos = provider.getMixerInfo();
 222                     // start from 0 to last device (do not reverse this order)
 223                     for (int ii = 0; ii < infos.length; ii++) {
 224                         try {
 225                             return provider.getMixer(infos[ii]);
 226                         } catch (IllegalArgumentException e) {
 227                             // this is not a good default device :)
 228                         }
 229                     }
 230                 } catch (IllegalArgumentException e) {
 231                 } catch (NullPointerException e) {
 232                 }
 233             }
 234         }
 235 
 236 
 237         throw new IllegalArgumentException("Mixer not supported: "
 238                                            + (info!=null?info.toString():"null"));
 239     }
 240 

 241     //$$fb 2002-11-26: fix for 4757930: DOC: AudioSystem.getTarget/SourceLineInfo() is ambiguous
 242 
 243     /**
 244      * Obtains information about all source lines of a particular type that are
 245      * supported by the installed mixers.





 246      *
 247      * @param  info a {@code Line.Info} object that specifies the kind of lines
 248      *         about which information is requested
 249      * @return an array of {@code Line.Info} objects describing source lines
 250      *         matching the type requested. If no matching source lines are
 251      *         supported, an array of length 0 is returned.
 252      * @see Mixer#getSourceLineInfo(Line.Info)
 253      */
 254     public static Line.Info[] getSourceLineInfo(Line.Info info) {
 255 
 256         Vector vector = new Vector();
 257         Line.Info[] currentInfoArray;
 258 
 259         Mixer mixer;
 260         Line.Info fullInfo = null;
 261         Mixer.Info[] infoArray = getMixerInfo();
 262 
 263         for (int i = 0; i < infoArray.length; i++) {
 264 
 265             mixer = getMixer(infoArray[i]);
 266 
 267             currentInfoArray = mixer.getSourceLineInfo(info);
 268             for (int j = 0; j < currentInfoArray.length; j++) {
 269                 vector.addElement(currentInfoArray[j]);
 270             }
 271         }
 272 
 273         Line.Info[] returnedArray = new Line.Info[vector.size()];
 274 
 275         for (int i = 0; i < returnedArray.length; i++) {
 276             returnedArray[i] = (Line.Info)vector.get(i);
 277         }
 278 
 279         return returnedArray;
 280     }
 281 

 282     /**
 283      * Obtains information about all target lines of a particular type that are
 284      * supported by the installed mixers.





 285      *
 286      * @param  info a {@code Line.Info} object that specifies the kind of lines
 287      *         about which information is requested
 288      * @return an array of {@code Line.Info} objects describing target lines
 289      *         matching the type requested. If no matching target lines are
 290      *         supported, an array of length 0 is returned.
 291      * @see Mixer#getTargetLineInfo(Line.Info)
 292      */
 293     public static Line.Info[] getTargetLineInfo(Line.Info info) {
 294 
 295         Vector vector = new Vector();
 296         Line.Info[] currentInfoArray;
 297 
 298         Mixer mixer;
 299         Line.Info fullInfo = null;
 300         Mixer.Info[] infoArray = getMixerInfo();
 301 
 302         for (int i = 0; i < infoArray.length; i++) {
 303 
 304             mixer = getMixer(infoArray[i]);
 305 
 306             currentInfoArray = mixer.getTargetLineInfo(info);
 307             for (int j = 0; j < currentInfoArray.length; j++) {
 308                 vector.addElement(currentInfoArray[j]);
 309             }
 310         }
 311 
 312         Line.Info[] returnedArray = new Line.Info[vector.size()];
 313 
 314         for (int i = 0; i < returnedArray.length; i++) {
 315             returnedArray[i] = (Line.Info)vector.get(i);
 316         }
 317 
 318         return returnedArray;
 319     }
 320 

 321     /**
 322      * Indicates whether the system supports any lines that match the specified
 323      * {@code Line.Info} object. A line is supported if any installed mixer
 324      * supports it.
 325      *
 326      * @param  info a {@code Line.Info} object describing the line for which
 327      *         support is queried
 328      * @return {@code true} if at least one matching line is supported,
 329      *         otherwise {@code false}
 330      * @see Mixer#isLineSupported(Line.Info)
 331      */
 332     public static boolean isLineSupported(Line.Info info) {
 333 
 334         Mixer mixer;
 335         Mixer.Info[] infoArray = getMixerInfo();
 336 
 337         for (int i = 0; i < infoArray.length; i++) {
 338 
 339             if( infoArray[i] != null ) {
 340                 mixer = getMixer(infoArray[i]);
 341                 if (mixer.isLineSupported(info)) {
 342                     return true;
 343                 }
 344             }
 345         }
 346 
 347         return false;
 348     }
 349 
 350     /**
 351      * Obtains a line that matches the description in the specified
 352      * {@code Line.Info} object.
 353      * <p>
 354      * If a {@code DataLine} is requested, and {@code info} is an instance of
 355      * {@code DataLine.Info} specifying at least one fully qualified audio
 356      * format, the last one will be used as the default format of the returned
 357      * {@code DataLine}.
 358      * <p>
 359      * If system properties
 360      * {@code javax.sound.sampled.Clip},
 361      * {@code javax.sound.sampled.Port},
 362      * {@code javax.sound.sampled.SourceDataLine} and
 363      * {@code javax.sound.sampled.TargetDataLine} are defined or they are
 364      * defined in the file "sound.properties", they are used to retrieve default
 365      * lines. For details, refer to the {@link AudioSystem class description}.
 366      *
 367      * If the respective property is not set, or the mixer requested in the
 368      * property is not installed or does not provide the requested line, all
 369      * installed mixers are queried for the requested line type. A Line will be
 370      * returned from the first mixer providing the requested line type.



 371      *
 372      * @param  info a {@code Line.Info} object describing the desired kind of
 373      *         line
 374      * @return a line of the requested kind
 375      * @throws LineUnavailableException if a matching line is not available due
 376      *         to resource restrictions
 377      * @throws SecurityException if a matching line is not available due to
 378      *         security restrictions
 379      * @throws IllegalArgumentException if the system does not support at least
 380      *         one line matching the specified {@code Line.Info} object through
 381      *         any installed mixer


 382      */
 383     public static Line getLine(Line.Info info) throws LineUnavailableException {
 384         LineUnavailableException lue = null;
 385         List providers = getMixerProviders();
 386 
 387 
 388         // 1: try from default mixer for this line class
 389         try {
 390             Mixer mixer = getDefaultMixer(providers, info);
 391             if (mixer != null && mixer.isLineSupported(info)) {
 392                 return mixer.getLine(info);
 393             }
 394         } catch (LineUnavailableException e) {
 395             lue = e;
 396         } catch (IllegalArgumentException iae) {
 397             // must not happen... but better to catch it here,
 398             // if plug-ins are badly written
 399         }
 400 
 401 


 436                     lue = e;
 437                 } catch (IllegalArgumentException iae) {
 438                     // must not happen... but better to catch it here,
 439                     // if plug-ins are badly written
 440                 }
 441             }
 442         }
 443 
 444         // if this line was supported but was not available, throw the last
 445         // LineUnavailableException we got (??).
 446         if (lue != null) {
 447             throw lue;
 448         }
 449 
 450         // otherwise, the requested line was not supported, so throw
 451         // an Illegal argument exception
 452         throw new IllegalArgumentException("No line matching " +
 453                                            info.toString() + " is supported.");
 454     }
 455 

 456     /**
 457      * Obtains a clip that can be used for playing back an audio file or an
 458      * audio stream. The returned clip will be provided by the default system
 459      * mixer, or, if not possible, by any other mixer installed in the system
 460      * that supports a {@code Clip} object.
 461      * <p>
 462      * The returned clip must be opened with the {@code open(AudioFormat)} or
 463      * {@code open(AudioInputStream)} method.
 464      * <p>
 465      * This is a high-level method that uses {@code getMixer} and
 466      * {@code getLine} internally.
 467      * <p>
 468      * If the system property {@code javax.sound.sampled.Clip} is defined or it
 469      * is defined in the file "sound.properties", it is used to retrieve the
 470      * default clip. For details, refer to the
 471      * {@link AudioSystem class description}.




 472      *
 473      * @return the desired clip object
 474      * @throws LineUnavailableException if a clip object is not available due to
 475      *         resource restrictions
 476      * @throws SecurityException if a clip object is not available due to
 477      *         security restrictions
 478      * @throws IllegalArgumentException if the system does not support at least
 479      *         one clip instance through any installed mixer


 480      * @see #getClip(Mixer.Info)
 481      * @since 1.5
 482      */
 483     public static Clip getClip() throws LineUnavailableException{
 484         AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
 485                                              AudioSystem.NOT_SPECIFIED,
 486                                              16, 2, 4,
 487                                              AudioSystem.NOT_SPECIFIED, true);
 488         DataLine.Info info = new DataLine.Info(Clip.class, format);
 489         return (Clip) AudioSystem.getLine(info);
 490     }
 491 

 492     /**
 493      * Obtains a clip from the specified mixer that can be used for playing back
 494      * an audio file or an audio stream.
 495      * <p>
 496      * The returned clip must be opened with the {@code open(AudioFormat)} or
 497      * {@code open(AudioInputStream)} method.
 498      * <p>
 499      * This is a high-level method that uses {@code getMixer} and
 500      * {@code getLine} internally.
 501      *
 502      * @param  mixerInfo a {@code Mixer.Info} object representing the desired
 503      *         mixer, or {@code null} for the system default mixer







 504      * @return a clip object from the specified mixer
 505      *
 506      * @throws LineUnavailableException if a clip is not available from this
 507      *         mixer due to resource restrictions
 508      * @throws SecurityException if a clip is not available from this mixer due
 509      *         to security restrictions
 510      * @throws IllegalArgumentException if the system does not support at least
 511      *         one clip through the specified mixer

 512      * @see #getClip()
 513      * @since 1.5
 514      */
 515     public static Clip getClip(Mixer.Info mixerInfo) throws LineUnavailableException{
 516         AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
 517                                              AudioSystem.NOT_SPECIFIED,
 518                                              16, 2, 4,
 519                                              AudioSystem.NOT_SPECIFIED, true);
 520         DataLine.Info info = new DataLine.Info(Clip.class, format);
 521         Mixer mixer = AudioSystem.getMixer(mixerInfo);
 522         return (Clip) mixer.getLine(info);
 523     }
 524 

 525     /**
 526      * Obtains a source data line that can be used for playing back audio data
 527      * in the format specified by the {@code AudioFormat} object. The returned
 528      * line will be provided by the default system mixer, or, if not possible,
 529      * by any other mixer installed in the system that supports a matching
 530      * {@code SourceDataLine} object.
 531      * <p>
 532      * The returned line should be opened with the {@code open(AudioFormat)} or
 533      * {@code open(AudioFormat, int)} method.
 534      * <p>
 535      * This is a high-level method that uses {@code getMixer} and
 536      * {@code getLine} internally.
 537      * <p>
 538      * The returned {@code SourceDataLine}'s default audio format will be
 539      * initialized with {@code format}.
 540      * <p>
 541      * If the system property {@code javax.sound.sampled.SourceDataLine} is
 542      * defined or it is defined in the file "sound.properties", it is used to
 543      * retrieve the default source data line. For details, refer to the
 544      * {@link AudioSystem class description}.
 545      *
 546      * @param  format an {@code AudioFormat} object specifying the supported
 547      *         audio format of the returned line, or {@code null} for any audio
 548      *         format
 549      * @return the desired {@code SourceDataLine} object
 550      * @throws LineUnavailableException if a matching source data line is not
 551      *         available due to resource restrictions
 552      * @throws SecurityException if a matching source data line is not available
 553      *         due to security restrictions
 554      * @throws IllegalArgumentException if the system does not support at least
 555      *         one source data line supporting the specified audio format
 556      *         through any installed mixer






 557      * @see #getSourceDataLine(AudioFormat, Mixer.Info)
 558      * @since 1.5
 559      */
 560     public static SourceDataLine getSourceDataLine(AudioFormat format)
 561         throws LineUnavailableException{
 562         DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
 563         return (SourceDataLine) AudioSystem.getLine(info);
 564     }
 565 

 566     /**
 567      * Obtains a source data line that can be used for playing back audio data
 568      * in the format specified by the {@code AudioFormat} object, provided by
 569      * the mixer specified by the {@code Mixer.Info} object.
 570      * <p>
 571      * The returned line should be opened with the {@code open(AudioFormat)} or
 572      * {@code open(AudioFormat, int)} method.
 573      * <p>
 574      * This is a high-level method that uses {@code getMixer} and
 575      * {@code getLine} internally.
 576      * <p>
 577      * The returned {@code SourceDataLine}'s default audio format will be
 578      * initialized with {@code format}.
 579      *
 580      * @param  format an {@code AudioFormat} object specifying the supported
 581      *         audio format of the returned line, or {@code null} for any audio
 582      *         format
 583      * @param  mixerinfo a {@code Mixer.Info} object representing the desired
 584      *         mixer, or {@code null} for the system default mixer
 585      * @return the desired {@code SourceDataLine} object
 586      * @throws LineUnavailableException if a matching source data line is not
 587      *         available from the specified mixer due to resource restrictions
 588      * @throws SecurityException if a matching source data line is not available
 589      *         from the specified mixer due to security restrictions
 590      * @throws IllegalArgumentException if the specified mixer does not support
 591      *         at least one source data line supporting the specified audio
 592      *         format







 593      * @see #getSourceDataLine(AudioFormat)
 594      * @since 1.5
 595      */
 596     public static SourceDataLine getSourceDataLine(AudioFormat format,
 597                                                    Mixer.Info mixerinfo)
 598         throws LineUnavailableException{
 599         DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
 600         Mixer mixer = AudioSystem.getMixer(mixerinfo);
 601         return (SourceDataLine) mixer.getLine(info);
 602     }
 603 

 604     /**
 605      * Obtains a target data line that can be used for recording audio data in
 606      * the format specified by the {@code AudioFormat} object. The returned line
 607      * will be provided by the default system mixer, or, if not possible, by any
 608      * other mixer installed in the system that supports a matching
 609      * {@code TargetDataLine} object.
 610      * <p>
 611      * The returned line should be opened with the {@code open(AudioFormat)} or
 612      * {@code open(AudioFormat, int)} method.
 613      * <p>
 614      * This is a high-level method that uses {@code getMixer} and
 615      * {@code getLine} internally.
 616      * <p>
 617      * The returned {@code TargetDataLine}'s default audio format will be
 618      * initialized with {@code format}.
 619      * <p>
 620      * If the system property {@code javax.sound.sampled.TargetDataLine} is
 621      * defined or it is defined in the file "sound.properties", it is used to
 622      * retrieve the default target data line. For details, refer to the
 623      * {@link AudioSystem class description}.
 624      *
 625      * @param  format an {@code AudioFormat} object specifying the supported
 626      *         audio format of the returned line, or {@code null} for any audio
 627      *         format
 628      * @return the desired {@code TargetDataLine} object
 629      * @throws LineUnavailableException if a matching target data line is not
 630      *         available due to resource restrictions
 631      * @throws SecurityException if a matching target data line is not available
 632      *         due to security restrictions
 633      * @throws IllegalArgumentException if the system does not support at least
 634      *         one target data line supporting the specified audio format
 635      *         through any installed mixer






 636      * @see #getTargetDataLine(AudioFormat, Mixer.Info)
 637      * @see AudioPermission
 638      * @since 1.5
 639      */
 640     public static TargetDataLine getTargetDataLine(AudioFormat format)
 641         throws LineUnavailableException{
 642 
 643         DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
 644         return (TargetDataLine) AudioSystem.getLine(info);
 645     }
 646 


 647     /**
 648      * Obtains a target data line that can be used for recording audio data in
 649      * the format specified by the {@code AudioFormat} object, provided by the
 650      * mixer specified by the {@code Mixer.Info} object.
 651      * <p>
 652      * The returned line should be opened with the {@code open(AudioFormat)} or
 653      * {@code open(AudioFormat, int)} method.
 654      * <p>
 655      * This is a high-level method that uses {@code getMixer} and
 656      * {@code getLine} internally.
 657      * <p>
 658      * The returned {@code TargetDataLine}'s default audio format will be
 659      * initialized with {@code format}.
 660      *
 661      * @param  format an {@code AudioFormat} object specifying the supported
 662      *         audio format of the returned line, or {@code null} for any audio
 663      *         format
 664      * @param  mixerinfo a {@code Mixer.Info} object representing the desired
 665      *         mixer, or {@code null} for the system default mixer
 666      * @return the desired {@code TargetDataLine} object
 667      * @throws LineUnavailableException if a matching target data line is not
 668      *         available from the specified mixer due to resource restrictions
 669      * @throws SecurityException if a matching target data line is not available
 670      *         from the specified mixer due to security restrictions
 671      * @throws IllegalArgumentException if the specified mixer does not support
 672      *         at least one target data line supporting the specified audio
 673      *         format






 674      * @see #getTargetDataLine(AudioFormat)
 675      * @see AudioPermission
 676      * @since 1.5
 677      */
 678     public static TargetDataLine getTargetDataLine(AudioFormat format,
 679                                                    Mixer.Info mixerinfo)
 680         throws LineUnavailableException {
 681 
 682         DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
 683         Mixer mixer = AudioSystem.getMixer(mixerinfo);
 684         return (TargetDataLine) mixer.getLine(info);
 685     }
 686 

 687     // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec
 688 
 689     /**
 690      * Obtains the encodings that the system can obtain from an audio input
 691      * stream with the specified encoding using the set of installed format
 692      * converters.
 693      *
 694      * @param  sourceEncoding the encoding for which conversion support is
 695      *         queried
 696      * @return array of encodings. If {@code sourceEncoding}is not supported, an
 697      *         array of length 0 is returned. Otherwise, the array will have a
 698      *         length of at least 1, representing {@code sourceEncoding}
 699      *         (no conversion).
 700      */
 701     public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding sourceEncoding) {
 702 
 703         List codecs = getFormatConversionProviders();
 704         Vector encodings = new Vector();
 705 
 706         AudioFormat.Encoding encs[] = null;
 707 
 708         // gather from all the codecs
 709         for(int i=0; i<codecs.size(); i++ ) {
 710             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 711             if( codec.isSourceEncodingSupported( sourceEncoding ) ) {
 712                 encs = codec.getTargetEncodings();
 713                 for (int j = 0; j < encs.length; j++) {
 714                     encodings.addElement( encs[j] );
 715                 }
 716             }
 717         }
 718         AudioFormat.Encoding encs2[] = (AudioFormat.Encoding[]) encodings.toArray(new AudioFormat.Encoding[0]);
 719         return encs2;
 720     }
 721 


 722     // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec
 723 
 724     /**
 725      * Obtains the encodings that the system can obtain from an audio input
 726      * stream with the specified format using the set of installed format
 727      * converters.
 728      *
 729      * @param  sourceFormat the audio format for which conversion is queried
 730      * @return array of encodings. If {@code sourceFormat}is not supported, an
 731      *         array of length 0 is returned. Otherwise, the array will have a
 732      *         length of at least 1, representing the encoding of
 733      *         {@code sourceFormat} (no conversion).
 734      */
 735     public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) {
 736 
 737 
 738         List codecs = getFormatConversionProviders();
 739         Vector encodings = new Vector();
 740 
 741         int size = 0;
 742         int index = 0;
 743         AudioFormat.Encoding encs[] = null;
 744 
 745         // gather from all the codecs
 746 
 747         for(int i=0; i<codecs.size(); i++ ) {
 748             encs = ((FormatConversionProvider) codecs.get(i)).getTargetEncodings(sourceFormat);
 749             size += encs.length;
 750             encodings.addElement( encs );
 751         }
 752 
 753         // now build a new array
 754 
 755         AudioFormat.Encoding encs2[] = new AudioFormat.Encoding[size];
 756         for(int i=0; i<encodings.size(); i++ ) {
 757             encs = (AudioFormat.Encoding [])(encodings.get(i));
 758             for(int j=0; j<encs.length; j++ ) {
 759                 encs2[index++] = encs[j];
 760             }
 761         }
 762         return encs2;
 763     }
 764 

 765     /**
 766      * Indicates whether an audio input stream of the specified encoding can be
 767      * obtained from an audio input stream that has the specified format.
 768      *
 769      * @param  targetEncoding the desired encoding after conversion
 770      * @param  sourceFormat the audio format before conversion
 771      * @return {@code true} if the conversion is supported, otherwise
 772      *         {@code false}
 773      */
 774     public static boolean isConversionSupported(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {
 775 
 776 
 777         List codecs = getFormatConversionProviders();
 778 
 779         for(int i=0; i<codecs.size(); i++ ) {
 780             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 781             if(codec.isConversionSupported(targetEncoding,sourceFormat) ) {
 782                 return true;
 783             }
 784         }
 785         return false;
 786     }
 787 

 788     /**
 789      * Obtains an audio input stream of the indicated encoding, by converting
 790      * the provided audio input stream.
 791      *
 792      * @param  targetEncoding the desired encoding after conversion
 793      * @param  sourceStream the stream to be converted
 794      * @return an audio input stream of the indicated encoding
 795      * @throws IllegalArgumentException if the conversion is not supported
 796      * @see #getTargetEncodings(AudioFormat.Encoding)
 797      * @see #getTargetEncodings(AudioFormat)
 798      * @see #isConversionSupported(AudioFormat.Encoding, AudioFormat)
 799      * @see #getAudioInputStream(AudioFormat, AudioInputStream)
 800      */
 801     public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding,
 802                                                        AudioInputStream sourceStream) {
 803 
 804         List codecs = getFormatConversionProviders();
 805 
 806         for(int i = 0; i < codecs.size(); i++) {
 807             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 808             if( codec.isConversionSupported( targetEncoding, sourceStream.getFormat() ) ) {
 809                 return codec.getAudioInputStream( targetEncoding, sourceStream );
 810             }
 811         }
 812         // we ran out of options, throw an exception
 813         throw new IllegalArgumentException("Unsupported conversion: " + targetEncoding + " from " + sourceStream.getFormat());
 814     }
 815 

 816     /**
 817      * Obtains the formats that have a particular encoding and that the system
 818      * can obtain from a stream of the specified format using the set of
 819      * installed format converters.
 820      *
 821      * @param  targetEncoding the desired encoding after conversion
 822      * @param  sourceFormat the audio format before conversion
 823      * @return array of formats. If no formats of the specified encoding are
 824      *         supported, an array of length 0 is returned.
 825      */
 826     public static AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {
 827 
 828         List codecs = getFormatConversionProviders();
 829         Vector formats = new Vector();
 830 
 831         int size = 0;
 832         int index = 0;
 833         AudioFormat fmts[] = null;
 834 
 835         // gather from all the codecs
 836 
 837         for(int i=0; i<codecs.size(); i++ ) {
 838             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 839             fmts = codec.getTargetFormats(targetEncoding, sourceFormat);
 840             size += fmts.length;
 841             formats.addElement( fmts );
 842         }
 843 
 844         // now build a new array
 845 
 846         AudioFormat fmts2[] = new AudioFormat[size];
 847         for(int i=0; i<formats.size(); i++ ) {
 848             fmts = (AudioFormat [])(formats.get(i));
 849             for(int j=0; j<fmts.length; j++ ) {
 850                 fmts2[index++] = fmts[j];
 851             }
 852         }
 853         return fmts2;
 854     }
 855 

 856     /**
 857      * Indicates whether an audio input stream of a specified format can be
 858      * obtained from an audio input stream of another specified format.
 859      *
 860      * @param  targetFormat the desired audio format after conversion
 861      * @param  sourceFormat the audio format before conversion
 862      * @return {@code true} if the conversion is supported, otherwise
 863      *         {@code false}
 864      */

 865     public static boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) {
 866 
 867         List codecs = getFormatConversionProviders();
 868 
 869         for(int i=0; i<codecs.size(); i++ ) {
 870             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 871             if(codec.isConversionSupported(targetFormat, sourceFormat) ) {
 872                 return true;
 873             }
 874         }
 875         return false;
 876     }
 877 

 878     /**
 879      * Obtains an audio input stream of the indicated format, by converting the
 880      * provided audio input stream.
 881      *
 882      * @param  targetFormat the desired audio format after conversion
 883      * @param  sourceStream the stream to be converted
 884      * @return an audio input stream of the indicated format
 885      * @throws IllegalArgumentException if the conversion is not supported
 886      * @see #getTargetEncodings(AudioFormat)
 887      * @see #getTargetFormats(AudioFormat.Encoding, AudioFormat)
 888      * @see #isConversionSupported(AudioFormat, AudioFormat)
 889      * @see #getAudioInputStream(AudioFormat.Encoding, AudioInputStream)
 890      */
 891     public static AudioInputStream getAudioInputStream(AudioFormat targetFormat,
 892                                                        AudioInputStream sourceStream) {
 893 
 894         if (sourceStream.getFormat().matches(targetFormat)) {
 895             return sourceStream;
 896         }
 897 
 898         List codecs = getFormatConversionProviders();
 899 
 900         for(int i = 0; i < codecs.size(); i++) {
 901             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 902             if(codec.isConversionSupported(targetFormat,sourceStream.getFormat()) ) {
 903                 return codec.getAudioInputStream(targetFormat,sourceStream);
 904             }
 905         }
 906 
 907         // we ran out of options...
 908         throw new IllegalArgumentException("Unsupported conversion: " + targetFormat + " from " + sourceStream.getFormat());
 909     }
 910 

 911     /**
 912      * Obtains the audio file format of the provided input stream. The stream
 913      * must point to valid audio file data. The implementation of this method
 914      * may require multiple parsers to examine the stream to determine whether
 915      * they support it. These parsers must be able to mark the stream, read
 916      * enough data to determine whether they support the stream, and, if not,
 917      * reset the stream's read pointer to its original position. If the input
 918      * stream does not support these operations, this method may fail with an
 919      * {@code IOException}.
 920      *
 921      * @param  stream the input stream from which file format information should
 922      *         be extracted
 923      * @return an {@code AudioFileFormat} object describing the stream's audio
 924      *         file format
 925      * @throws UnsupportedAudioFileException if the stream does not point to
 926      *         valid audio file data recognized by the system
 927      * @throws IOException if an input/output exception occurs
 928      * @see InputStream#markSupported
 929      * @see InputStream#mark
 930      */
 931     public static AudioFileFormat getAudioFileFormat(InputStream stream)
 932         throws UnsupportedAudioFileException, IOException {
 933 
 934         List providers = getAudioFileReaders();
 935         AudioFileFormat format = null;
 936 
 937         for(int i = 0; i < providers.size(); i++ ) {
 938             AudioFileReader reader = (AudioFileReader) providers.get(i);
 939             try {
 940                 format = reader.getAudioFileFormat( stream ); // throws IOException
 941                 break;
 942             } catch (UnsupportedAudioFileException e) {
 943                 continue;
 944             }
 945         }
 946 
 947         if( format==null ) {
 948             throw new UnsupportedAudioFileException("file is not a supported file type");
 949         } else {
 950             return format;
 951         }
 952     }
 953 
 954     /**
 955      * Obtains the audio file format of the specified URL. The URL must point to
 956      * valid audio file data.
 957      *
 958      * @param  url the URL from which file format information should be
 959      *         extracted
 960      * @return an {@code AudioFileFormat} object describing the audio file
 961      *         format
 962      * @throws UnsupportedAudioFileException if the URL does not point to valid
 963      *         audio file data recognized by the system
 964      * @throws IOException if an input/output exception occurs
 965      */
 966     public static AudioFileFormat getAudioFileFormat(URL url)
 967         throws UnsupportedAudioFileException, IOException {
 968 
 969         List providers = getAudioFileReaders();
 970         AudioFileFormat format = null;
 971 
 972         for(int i = 0; i < providers.size(); i++ ) {
 973             AudioFileReader reader = (AudioFileReader) providers.get(i);
 974             try {
 975                 format = reader.getAudioFileFormat( url ); // throws IOException
 976                 break;
 977             } catch (UnsupportedAudioFileException e) {
 978                 continue;
 979             }
 980         }
 981 
 982         if( format==null ) {
 983             throw new UnsupportedAudioFileException("file is not a supported file type");
 984         } else {
 985             return format;
 986         }
 987     }
 988 
 989     /**
 990      * Obtains the audio file format of the specified {@code File}. The
 991      * {@code File} must point to valid audio file data.
 992      *
 993      * @param  file the {@code File} from which file format information should
 994      *         be extracted
 995      * @return an {@code AudioFileFormat} object describing the audio file
 996      *         format
 997      * @throws UnsupportedAudioFileException if the {@code File} does not point
 998      *         to valid audio file data recognized by the system
 999      * @throws IOException if an I/O exception occurs
1000      */
1001     public static AudioFileFormat getAudioFileFormat(File file)
1002         throws UnsupportedAudioFileException, IOException {
1003 
1004         List providers = getAudioFileReaders();
1005         AudioFileFormat format = null;
1006 
1007         for(int i = 0; i < providers.size(); i++ ) {
1008             AudioFileReader reader = (AudioFileReader) providers.get(i);
1009             try {
1010                 format = reader.getAudioFileFormat( file ); // throws IOException
1011                 break;
1012             } catch (UnsupportedAudioFileException e) {
1013                 continue;
1014             }
1015         }
1016 
1017         if( format==null ) {
1018             throw new UnsupportedAudioFileException("file is not a supported file type");
1019         } else {
1020             return format;
1021         }
1022     }
1023 

1024     /**
1025      * Obtains an audio input stream from the provided input stream. The stream
1026      * must point to valid audio file data. The implementation of this method
1027      * may require multiple parsers to examine the stream to determine whether
1028      * they support it. These parsers must be able to mark the stream, read
1029      * enough data to determine whether they support the stream, and, if not,
1030      * reset the stream's read pointer to its original position. If the input
1031      * stream does not support these operation, this method may fail with an
1032      * {@code IOException}.
1033      *
1034      * @param  stream the input stream from which the {@code AudioInputStream}
1035      *         should be constructed
1036      * @return an {@code AudioInputStream} object based on the audio file data
1037      *         contained in the input stream
1038      * @throws UnsupportedAudioFileException if the stream does not point to
1039      *         valid audio file data recognized by the system
1040      * @throws IOException if an I/O exception occurs
1041      * @see InputStream#markSupported
1042      * @see InputStream#mark
1043      */
1044     public static AudioInputStream getAudioInputStream(InputStream stream)
1045         throws UnsupportedAudioFileException, IOException {
1046 
1047         List providers = getAudioFileReaders();
1048         AudioInputStream audioStream = null;
1049 
1050         for(int i = 0; i < providers.size(); i++ ) {
1051             AudioFileReader reader = (AudioFileReader) providers.get(i);
1052             try {
1053                 audioStream = reader.getAudioInputStream( stream ); // throws IOException
1054                 break;
1055             } catch (UnsupportedAudioFileException e) {
1056                 continue;
1057             }
1058         }
1059 
1060         if( audioStream==null ) {
1061             throw new UnsupportedAudioFileException("could not get audio input stream from input stream");
1062         } else {
1063             return audioStream;
1064         }
1065     }
1066 
1067     /**
1068      * Obtains an audio input stream from the URL provided. The URL must point
1069      * to valid audio file data.
1070      *
1071      * @param  url the URL for which the {@code AudioInputStream} should be
1072      *         constructed
1073      * @return an {@code AudioInputStream} object based on the audio file data
1074      *         pointed to by the URL
1075      * @throws UnsupportedAudioFileException if the URL does not point to valid
1076      *         audio file data recognized by the system
1077      * @throws IOException if an I/O exception occurs
1078      */
1079     public static AudioInputStream getAudioInputStream(URL url)
1080         throws UnsupportedAudioFileException, IOException {
1081 
1082         List providers = getAudioFileReaders();
1083         AudioInputStream audioStream = null;
1084 
1085         for(int i = 0; i < providers.size(); i++ ) {
1086             AudioFileReader reader = (AudioFileReader) providers.get(i);
1087             try {
1088                 audioStream = reader.getAudioInputStream( url ); // throws IOException
1089                 break;
1090             } catch (UnsupportedAudioFileException e) {
1091                 continue;
1092             }
1093         }
1094 
1095         if( audioStream==null ) {
1096             throw new UnsupportedAudioFileException("could not get audio input stream from input URL");
1097         } else {
1098             return audioStream;
1099         }
1100     }
1101 
1102     /**
1103      * Obtains an audio input stream from the provided {@code File}. The
1104      * {@code File} must point to valid audio file data.
1105      *
1106      * @param  file the {@code File} for which the {@code AudioInputStream}
1107      *         should be constructed
1108      * @return an {@code AudioInputStream} object based on the audio file data
1109      *         pointed to by the {@code File}
1110      * @throws UnsupportedAudioFileException if the {@code File} does not point
1111      *         to valid audio file data recognized by the system
1112      * @throws IOException if an I/O exception occurs
1113      */
1114     public static AudioInputStream getAudioInputStream(File file)
1115         throws UnsupportedAudioFileException, IOException {
1116 
1117         List providers = getAudioFileReaders();
1118         AudioInputStream audioStream = null;
1119 
1120         for(int i = 0; i < providers.size(); i++ ) {
1121             AudioFileReader reader = (AudioFileReader) providers.get(i);
1122             try {
1123                 audioStream = reader.getAudioInputStream( file ); // throws IOException
1124                 break;
1125             } catch (UnsupportedAudioFileException e) {
1126                 continue;
1127             }
1128         }
1129 
1130         if( audioStream==null ) {
1131             throw new UnsupportedAudioFileException("could not get audio input stream from input file");
1132         } else {
1133             return audioStream;
1134         }
1135     }
1136 

1137     /**
1138      * Obtains the file types for which file writing support is provided by the
1139      * system.
1140      *
1141      * @return array of unique file types. If no file types are supported, an
1142      *         array of length 0 is returned.
1143      */
1144     public static AudioFileFormat.Type[] getAudioFileTypes() {
1145         List providers = getAudioFileWriters();
1146         Set returnTypesSet = new HashSet();
1147 
1148         for(int i=0; i < providers.size(); i++) {
1149             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1150             AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes();
1151             for(int j=0; j < fileTypes.length; j++) {
1152                 returnTypesSet.add(fileTypes[j]);
1153             }
1154         }
1155         AudioFileFormat.Type returnTypes[] = (AudioFileFormat.Type[])
1156             returnTypesSet.toArray(new AudioFileFormat.Type[0]);
1157         return returnTypes;
1158     }
1159 

1160     /**
1161      * Indicates whether file writing support for the specified file type is
1162      * provided by the system.
1163      *
1164      * @param  fileType the file type for which write capabilities are queried
1165      * @return {@code true} if the file type is supported, otherwise
1166      *         {@code false}
1167      */
1168     public static boolean isFileTypeSupported(AudioFileFormat.Type fileType) {
1169 
1170         List providers = getAudioFileWriters();
1171 
1172         for(int i=0; i < providers.size(); i++) {
1173             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1174             if (writer.isFileTypeSupported(fileType)) {
1175                 return true;
1176             }
1177         }
1178         return false;
1179     }
1180 

1181     /**
1182      * Obtains the file types that the system can write from the audio input
1183      * stream specified.
1184      *
1185      * @param  stream the audio input stream for which audio file type
1186      *         support is queried
1187      * @return array of file types. If no file types are supported, an array of
1188      *         length 0 is returned.
1189      */
1190     public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
1191         List providers = getAudioFileWriters();
1192         Set returnTypesSet = new HashSet();
1193 
1194         for(int i=0; i < providers.size(); i++) {
1195             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1196             AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes(stream);
1197             for(int j=0; j < fileTypes.length; j++) {
1198                 returnTypesSet.add(fileTypes[j]);
1199             }
1200         }
1201         AudioFileFormat.Type returnTypes[] = (AudioFileFormat.Type[])
1202             returnTypesSet.toArray(new AudioFileFormat.Type[0]);
1203         return returnTypes;
1204     }
1205 

1206     /**
1207      * Indicates whether an audio file of the specified file type can be written
1208      * from the indicated audio input stream.
1209      *
1210      * @param  fileType the file type for which write capabilities are queried
1211      * @param  stream the stream for which file-writing support is queried
1212      * @return {@code true} if the file type is supported for this audio input
1213      *         stream, otherwise {@code false}
1214      */
1215     public static boolean isFileTypeSupported(AudioFileFormat.Type fileType,
1216                                               AudioInputStream stream) {
1217 
1218         List providers = getAudioFileWriters();
1219 
1220         for(int i=0; i < providers.size(); i++) {
1221             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1222             if(writer.isFileTypeSupported(fileType, stream)) {
1223                 return true;
1224             }
1225         }
1226         return false;
1227     }
1228 

1229     /**
1230      * Writes a stream of bytes representing an audio file of the specified file
1231      * type to the output stream provided. Some file types require that the
1232      * length be written into the file header; such files cannot be written from
1233      * start to finish unless the length is known in advance. An attempt to
1234      * write a file of such a type will fail with an IOException if the length
1235      * in the audio file type is {@code AudioSystem.NOT_SPECIFIED}.
1236      *
1237      * @param  stream the audio input stream containing audio data to be written
1238      *         to the file
1239      * @param  fileType the kind of audio file to write
1240      * @param  out the stream to which the file data should be written
1241      * @return the number of bytes written to the output stream
1242      * @throws IOException if an input/output exception occurs
1243      * @throws IllegalArgumentException if the file type is not supported by the
1244      *         system
1245      * @see #isFileTypeSupported
1246      * @see #getAudioFileTypes
1247      */
1248     public static int write(AudioInputStream stream, AudioFileFormat.Type fileType,
1249                             OutputStream out) throws IOException {
1250 
1251         List providers = getAudioFileWriters();
1252         int bytesWritten = 0;
1253         boolean flag = false;
1254 
1255         for(int i=0; i < providers.size(); i++) {
1256             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1257             try {
1258                 bytesWritten = writer.write( stream, fileType, out ); // throws IOException
1259                 flag = true;
1260                 break;
1261             } catch (IllegalArgumentException e) {
1262                 // thrown if this provider cannot write the sequence, try the next
1263                 continue;
1264             }
1265         }
1266         if(!flag) {
1267             throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType);
1268         } else {
1269             return bytesWritten;
1270         }
1271     }
1272 

1273     /**
1274      * Writes a stream of bytes representing an audio file of the specified file
1275      * type to the external file provided.
1276      *
1277      * @param  stream the audio input stream containing audio data to be written
1278      *         to the file
1279      * @param  fileType the kind of audio file to write
1280      * @param  out the external file to which the file data should be written
1281      * @return the number of bytes written to the file
1282      * @throws IOException if an I/O exception occurs
1283      * @throws IllegalArgumentException if the file type is not supported by the
1284      *         system
1285      * @see #isFileTypeSupported
1286      * @see #getAudioFileTypes
1287      */
1288     public static int write(AudioInputStream stream, AudioFileFormat.Type fileType,
1289                             File out) throws IOException {
1290 
1291         List providers = getAudioFileWriters();
1292         int bytesWritten = 0;
1293         boolean flag = false;
1294 
1295         for(int i=0; i < providers.size(); i++) {
1296             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1297             try {
1298                 bytesWritten = writer.write( stream, fileType, out ); // throws IOException
1299                 flag = true;
1300                 break;
1301             } catch (IllegalArgumentException e) {
1302                 // thrown if this provider cannot write the sequence, try the next
1303                 continue;
1304             }
1305         }
1306         if (!flag) {
1307             throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType);
1308         } else {
1309             return bytesWritten;
1310         }
1311     }
1312 

1313     // METHODS FOR INTERNAL IMPLEMENTATION USE
1314 
1315     /**
1316      * Obtains the set of MixerProviders on the system.
1317      */
1318     private static List getMixerProviders() {
1319         return getProviders(MixerProvider.class);
1320     }
1321 

1322     /**
1323      * Obtains the set of format converters (codecs, transcoders, etc.) that are
1324      * currently installed on the system.
1325      *
1326      * @return an array of {@link javax.sound.sampled.spi.FormatConversionProvider
1327      *         FormatConversionProvider} objects representing the available
1328      *         format converters. If no format converters readers are available
1329      *         on the system, an array of length 0 is returned.

1330      */
1331     private static List getFormatConversionProviders() {
1332         return getProviders(FormatConversionProvider.class);
1333     }
1334 

1335     /**
1336      * Obtains the set of audio file readers that are currently installed on the
1337      * system.
1338      *
1339      * @return a List of {@link javax.sound.sampled.spi.AudioFileReader
1340      *         AudioFileReader} objects representing the installed audio file
1341      *         readers. If no audio file readers are available on the system, an
1342      *         empty List is returned.
1343      */
1344     private static List getAudioFileReaders() {
1345         return getProviders(AudioFileReader.class);
1346     }
1347 

1348     /**
1349      * Obtains the set of audio file writers that are currently installed on the
1350      * system.
1351      *
1352      * @return a List of {@link javax.sound.sampled.spi.AudioFileWriter
1353      *         AudioFileWriter} objects representing the available audio file
1354      *         writers. If no audio file writers are available on the system, an
1355      *         empty List is returned.
1356      */
1357     private static List getAudioFileWriters() {
1358         return getProviders(AudioFileWriter.class);
1359     }
1360 
1361     /**
1362      * Attempts to locate and return a default Mixer that provides lines of the
1363      * specified type.

1364      *
1365      * @param  providers the installed mixer providers
1366      * @param  info The requested line type TargetDataLine.class, Clip.class or
1367      *         Port.class
1368      * @return a Mixer that matches the requirements, or null if no default
1369      *         mixer found
1370      */
1371     private static Mixer getDefaultMixer(List providers, Line.Info info) {
1372         Class lineClass = info.getLineClass();
1373         String providerClassName = JDK13Services.getDefaultProviderClassName(lineClass);
1374         String instanceName = JDK13Services.getDefaultInstanceName(lineClass);
1375         Mixer mixer;
1376 
1377         if (providerClassName != null) {
1378             MixerProvider defaultProvider = getNamedProvider(providerClassName, providers);
1379             if (defaultProvider != null) {
1380                 if (instanceName != null) {
1381                     mixer = getNamedMixer(instanceName, defaultProvider, info);
1382                     if (mixer != null) {
1383                         return mixer;
1384                     }
1385                 } else {
1386                     mixer = getFirstMixer(defaultProvider, info,
1387                                           false /* mixing not required*/);
1388                     if (mixer != null) {
1389                         return mixer;


1392 
1393             }
1394         }
1395 
1396         /* Provider class not specified or
1397            provider class cannot be found, or
1398            provider class and instance specified and instance cannot be found or is not appropriate */
1399         if (instanceName != null) {
1400             mixer = getNamedMixer(instanceName, providers, info);
1401             if (mixer != null) {
1402                 return mixer;
1403             }
1404         }
1405 
1406 
1407         /* No default are specified, or if something is specified, everything
1408            failed. */
1409         return null;
1410     }
1411 
1412     /**
1413      * Return a MixerProvider of a given class from the list of MixerProviders.
1414      * This method never requires the returned Mixer to do mixing.
1415      *
1416      * @param  providerClassName The class name of the provider to be returned
1417      * @param  providers The list of MixerProviders that is searched
1418      * @return A MixerProvider of the requested class, or null if none is found



1419      */
1420     private static MixerProvider getNamedProvider(String providerClassName,
1421                                                   List providers) {
1422         for(int i = 0; i < providers.size(); i++) {
1423             MixerProvider provider = (MixerProvider) providers.get(i);
1424             if (provider.getClass().getName().equals(providerClassName)) {
1425                 return provider;
1426             }
1427         }
1428         return null;
1429     }
1430 
1431     /**
1432      * Return a Mixer with a given name from a given MixerProvider. This method
1433      * never requires the returned Mixer to do mixing.
1434      *
1435      * @param  mixerName The name of the Mixer to be returned
1436      * @param  provider The MixerProvider to check for Mixers
1437      * @param  info The type of line the returned Mixer is required to support
1438      * @return A Mixer matching the requirements, or null if none is found

1439      */
1440     private static Mixer getNamedMixer(String mixerName,
1441                                        MixerProvider provider,
1442                                        Line.Info info) {
1443         Mixer.Info[] infos = provider.getMixerInfo();
1444         for (int i = 0; i < infos.length; i++) {
1445             if (infos[i].getName().equals(mixerName)) {
1446                 Mixer mixer = provider.getMixer(infos[i]);
1447                 if (isAppropriateMixer(mixer, info, false)) {
1448                     return mixer;
1449                 }
1450             }
1451         }
1452         return null;
1453     }
1454 
1455     /**
1456      * From a List of MixerProviders, return a Mixer with a given name. This
1457      * method never requires the returned Mixer to do mixing.
1458      *
1459      * @param  mixerName The name of the Mixer to be returned
1460      * @param  providers The List of MixerProviders to check for Mixers
1461      * @param  info The type of line the returned Mixer is required to support
1462      * @return A Mixer matching the requirements, or null if none is found
1463      */
1464     private static Mixer getNamedMixer(String mixerName,
1465                                        List providers,
1466                                        Line.Info info) {
1467         for(int i = 0; i < providers.size(); i++) {
1468             MixerProvider provider = (MixerProvider) providers.get(i);
1469             Mixer mixer = getNamedMixer(mixerName, provider, info);
1470             if (mixer != null) {
1471                 return mixer;
1472             }
1473         }
1474         return null;
1475     }
1476 
1477     /**
1478      * From a given MixerProvider, return the first appropriate Mixer.
1479      *
1480      * @param  provider The MixerProvider to check for Mixers
1481      * @param  info The type of line the returned Mixer is required to support
1482      * @param  isMixingRequired If true, only Mixers that support mixing are
1483      *         returned for line types of SourceDataLine and Clip
1484      * @return A Mixer that is considered appropriate, or null if none is found


1485      */
1486     private static Mixer getFirstMixer(MixerProvider provider,
1487                                        Line.Info info,
1488                                        boolean isMixingRequired) {
1489         Mixer.Info[] infos = provider.getMixerInfo();
1490         for (int j = 0; j < infos.length; j++) {
1491             Mixer mixer = provider.getMixer(infos[j]);
1492             if (isAppropriateMixer(mixer, info, isMixingRequired)) {
1493                 return mixer;
1494             }
1495         }
1496         return null;
1497     }
1498 
1499     /**
1500      * Checks if a Mixer is appropriate. A Mixer is considered appropriate if it
1501      * support the given line type. If isMixingRequired is true and the line
1502      * type is an output one (SourceDataLine, Clip), the mixer is appropriate if
1503      * it supports at least 2 (concurrent) lines of the given type.
1504      *
1505      * @return {@code true} if the mixer is considered appropriate according to
1506      *         the rules given above, {@code false} otherwise

1507      */
1508     private static boolean isAppropriateMixer(Mixer mixer,
1509                                               Line.Info lineInfo,
1510                                               boolean isMixingRequired) {
1511         if (! mixer.isLineSupported(lineInfo)) {
1512             return false;
1513         }
1514         Class lineClass = lineInfo.getLineClass();
1515         if (isMixingRequired
1516             && (SourceDataLine.class.isAssignableFrom(lineClass) ||
1517                 Clip.class.isAssignableFrom(lineClass))) {
1518             int maxLines = mixer.getMaxLines(lineInfo);
1519             return ((maxLines == NOT_SPECIFIED) || (maxLines > 1));
1520         }
1521         return true;
1522     }
1523 


1524     /**
1525      * Like getMixerInfo, but return List.
1526      */
1527     private static List getMixerInfoList() {
1528         List providers = getMixerProviders();
1529         return getMixerInfoList(providers);
1530     }
1531 

1532     /**
1533      * Like getMixerInfo, but return List.
1534      */
1535     private static List getMixerInfoList(List providers) {
1536         List infos = new ArrayList();
1537 
1538         Mixer.Info[] someInfos; // per-mixer
1539         Mixer.Info[] allInfos;  // for all mixers
1540 
1541         for(int i = 0; i < providers.size(); i++ ) {
1542             someInfos = ((MixerProvider)providers.get(i)).getMixerInfo();
1543 
1544             for (int j = 0; j < someInfos.length; j++) {
1545                 infos.add(someInfos[j]);
1546             }
1547         }
1548 
1549         return infos;
1550     }
1551 

1552     /**
1553      * Obtains the set of services currently installed on the system using the
1554      * SPI mechanism in 1.3.
1555      *
1556      * @return a List of instances of providers for the requested service. If no
1557      *         providers are available, a vector of length 0 will be returned.
1558      */
1559     private static List getProviders(Class providerClass) {
1560         return JDK13Services.getProviders(providerClass);
1561     }
1562 }