1 /*
   2  * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.sound.sampled;
  27 
  28 import java.io.File;
  29 import java.io.InputStream;
  30 import java.io.IOException;
  31 import java.io.OutputStream;
  32 import java.net.URL;
  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  * In the Oracle reference implementation, the properties file is
  67  * &quot;lib/sound.properties&quot; in the JRE
  68  * directory. If a property exists both as a system property and in the
  69  * properties file, the system property takes precedence. If none is
  70  * specified, a suitable default is chosen among the available devices.
  71  * The syntax of the properties file is specified in
  72  * {@link java.util.Properties#load(InputStream) Properties.load}. The
  73  * following table lists the available property keys and which methods
  74  * consider them:
  75  *
  76  * <table border=0>
  77  *  <tr>
  78  *   <th>Property Key</th>
  79  *   <th>Interface</th>
  80  *   <th>Affected Method(s)</th>
  81  *  </tr>
  82  *  <tr>
  83  *   <td><code>javax.sound.sampled.Clip</code></td>
  84  *   <td>{@link Clip}</td>
  85  *   <td>{@link #getLine}, {@link #getClip}</td>
  86  *  </tr>
  87  *  <tr>
  88  *   <td><code>javax.sound.sampled.Port</code></td>
  89  *   <td>{@link Port}</td>
  90  *   <td>{@link #getLine}</td>
  91  *  </tr>
  92  *  <tr>
  93  *   <td><code>javax.sound.sampled.SourceDataLine</code></td>
  94  *   <td>{@link SourceDataLine}</td>
  95  *   <td>{@link #getLine}, {@link #getSourceDataLine}</td>
  96  *  </tr>
  97  *  <tr>
  98  *   <td><code>javax.sound.sampled.TargetDataLine</code></td>
  99  *   <td>{@link TargetDataLine}</td>
 100  *   <td>{@link #getLine}, {@link #getTargetDataLine}</td>
 101  *  </tr>
 102  * </table>
 103  *
 104  * The property value consists of the provider class name
 105  * and the mixer name, separated by the hash mark (&quot;#&quot;).
 106  * The provider class name is the fully-qualified
 107  * name of a concrete {@link javax.sound.sampled.spi.MixerProvider
 108  * mixer provider} class. The mixer name is matched against
 109  * the <code>String</code> returned by the <code>getName</code>
 110  * method of <code>Mixer.Info</code>.
 111  * Either the class name, or the mixer name may be omitted.
 112  * If only the class name is specified, the trailing hash mark
 113  * is optional.
 114  *
 115  * <p>If the provider class is specified, and it can be
 116  * successully retrieved from the installed providers, the list of
 117  * <code>Mixer.Info</code> objects is retrieved
 118  * from the provider. Otherwise, or when these mixers
 119  * do not provide a subsequent match, the list is retrieved
 120  * from {@link #getMixerInfo} to contain
 121  * all available <code>Mixer.Info</code> objects.
 122  *
 123  * <p>If a mixer name is specified, the resulting list of
 124  * <code>Mixer.Info</code> objects is searched:
 125  * the first one with a matching name, and whose
 126  * <code>Mixer</code> provides the
 127  * respective line interface, will be returned.
 128  * If no matching <code>Mixer.Info</code> object
 129  * is found, or the mixer name is not specified,
 130  * the first mixer from the resulting
 131  * list, which provides the respective line
 132  * interface, will be returned.
 133  *
 134  * For example, the property <code>javax.sound.sampled.Clip</code>
 135  * with a value
 136  * <code>&quot;com.sun.media.sound.MixerProvider#SunClip&quot;</code>
 137  * will have the following consequences when
 138  * <code>getLine</code> is called requesting a <code>Clip</code>
 139  * instance:
 140  * if the class <code>com.sun.media.sound.MixerProvider</code> exists
 141  * in the list of installed mixer providers,
 142  * the first <code>Clip</code> from the first mixer with name
 143  * <code>&quot;SunClip&quot;</code> will be returned. If it cannot
 144  * be found, the first <code>Clip</code> from the first mixer
 145  * of the specified provider will be returned, regardless of name.
 146  * If there is none, the first <code>Clip</code> from the first
 147  * <code>Mixer</code> with name
 148  * <code>&quot;SunClip&quot;</code> in the list of all mixers
 149  * (as returned by <code>getMixerInfo</code>) will be returned,
 150  * or, if not found, the first <code>Clip</code> of the first
 151  * <code>Mixer</code>that can be found in the list of all
 152  * mixers is returned.
 153  * If that fails, too, an <code>IllegalArgumentException</code>
 154  * is thrown.
 155  *
 156  * @author Kara Kytle
 157  * @author Florian Bomers
 158  * @author Matthias Pfisterer
 159  * @author Kevin P. Smith
 160  *
 161  * @see AudioFormat
 162  * @see AudioInputStream
 163  * @see Mixer
 164  * @see Line
 165  * @see Line.Info
 166  * @since 1.3
 167  */
 168 public class AudioSystem {
 169 
 170     /**
 171      * An integer that stands for an unknown numeric value.
 172      * This value is appropriate only for signed quantities that do not
 173      * normally take negative values.  Examples include file sizes, frame
 174      * sizes, buffer sizes, and sample rates.
 175      * A number of Java Sound constructors accept
 176      * a value of <code>NOT_SPECIFIED</code> for such parameters.  Other
 177      * methods may also accept or return this value, as documented.
 178      */
 179     public static final int NOT_SPECIFIED = -1;
 180 
 181     /**
 182      * Private no-args constructor for ensuring against instantiation.
 183      */
 184     private AudioSystem() {
 185     }
 186 
 187 
 188     /**
 189      * Obtains an array of mixer info objects that represents
 190      * the set of audio mixers that are currently installed on the system.
 191      * @return an array of info objects for the currently installed mixers.  If no mixers
 192      * are available on the system, an array of length 0 is returned.
 193      * @see #getMixer
 194      */
 195     public static Mixer.Info[] getMixerInfo() {
 196 
 197         List infos = getMixerInfoList();
 198         Mixer.Info[] allInfos = (Mixer.Info[]) infos.toArray(new Mixer.Info[infos.size()]);
 199         return allInfos;
 200     }
 201 
 202 
 203     /**
 204      * Obtains the requested audio mixer.
 205      * @param info a <code>Mixer.Info</code> object representing the desired
 206      * mixer, or <code>null</code> for the system default mixer
 207      * @return the requested mixer
 208      * @throws SecurityException if the requested mixer
 209      * is unavailable because of security restrictions
 210      * @throws IllegalArgumentException if the info object does not represent
 211      * a mixer installed on the system
 212      * @see #getMixerInfo
 213      */
 214     public static Mixer getMixer(Mixer.Info info) {
 215 
 216         Mixer mixer = null;
 217         List providers = getMixerProviders();
 218 
 219         for(int i = 0; i < providers.size(); i++ ) {
 220 
 221             try {
 222                 return ((MixerProvider)providers.get(i)).getMixer(info);
 223 
 224             } catch (IllegalArgumentException e) {
 225             } catch (NullPointerException e) {
 226                 // $$jb 08.20.99:  If the strings in the info object aren't
 227                 // set, then Netscape (using jdk1.1.5) tends to throw
 228                 // NPE's when doing some string manipulation.  This is
 229                 // probably not the best fix, but is solves the problem
 230                 // of the NPE in Netscape using local classes
 231                 // $$jb 11.01.99: Replacing this patch.
 232             }
 233         }
 234 
 235         //$$fb if looking for default mixer, and not found yet, add a round of looking
 236         if (info == null) {
 237             for(int i = 0; i < providers.size(); i++ ) {
 238                 try {
 239                     MixerProvider provider = (MixerProvider) providers.get(i);
 240                     Mixer.Info[] infos = provider.getMixerInfo();
 241                     // start from 0 to last device (do not reverse this order)
 242                     for (int ii = 0; ii < infos.length; ii++) {
 243                         try {
 244                             return provider.getMixer(infos[ii]);
 245                         } catch (IllegalArgumentException e) {
 246                             // this is not a good default device :)
 247                         }
 248                     }
 249                 } catch (IllegalArgumentException e) {
 250                 } catch (NullPointerException e) {
 251                 }
 252             }
 253         }
 254 
 255 
 256         throw new IllegalArgumentException("Mixer not supported: "
 257                                            + (info!=null?info.toString():"null"));
 258     }
 259 
 260 
 261     //$$fb 2002-11-26: fix for 4757930: DOC: AudioSystem.getTarget/SourceLineInfo() is ambiguous
 262     /**
 263      * Obtains information about all source lines of a particular type that are supported
 264      * by the installed mixers.
 265      * @param info a <code>Line.Info</code> object that specifies the kind of
 266      * lines about which information is requested
 267      * @return an array of <code>Line.Info</code> objects describing source lines matching
 268      * the type requested.  If no matching source lines are supported, an array of length 0
 269      * is returned.
 270      *
 271      * @see Mixer#getSourceLineInfo(Line.Info)
 272      */
 273     public static Line.Info[] getSourceLineInfo(Line.Info info) {
 274 
 275         Vector vector = new Vector();
 276         Line.Info[] currentInfoArray;
 277 
 278         Mixer mixer;
 279         Line.Info fullInfo = null;
 280         Mixer.Info[] infoArray = getMixerInfo();
 281 
 282         for (int i = 0; i < infoArray.length; i++) {
 283 
 284             mixer = getMixer(infoArray[i]);
 285 
 286             currentInfoArray = mixer.getSourceLineInfo(info);
 287             for (int j = 0; j < currentInfoArray.length; j++) {
 288                 vector.addElement(currentInfoArray[j]);
 289             }
 290         }
 291 
 292         Line.Info[] returnedArray = new Line.Info[vector.size()];
 293 
 294         for (int i = 0; i < returnedArray.length; i++) {
 295             returnedArray[i] = (Line.Info)vector.get(i);
 296         }
 297 
 298         return returnedArray;
 299     }
 300 
 301 
 302     /**
 303      * Obtains information about all target lines of a particular type that are supported
 304      * by the installed mixers.
 305      * @param info a <code>Line.Info</code> object that specifies the kind of
 306      * lines about which information is requested
 307      * @return an array of <code>Line.Info</code> objects describing target lines matching
 308      * the type requested.  If no matching target lines are supported, an array of length 0
 309      * is returned.
 310      *
 311      * @see Mixer#getTargetLineInfo(Line.Info)
 312      */
 313     public static Line.Info[] getTargetLineInfo(Line.Info info) {
 314 
 315         Vector vector = new Vector();
 316         Line.Info[] currentInfoArray;
 317 
 318         Mixer mixer;
 319         Line.Info fullInfo = null;
 320         Mixer.Info[] infoArray = getMixerInfo();
 321 
 322         for (int i = 0; i < infoArray.length; i++) {
 323 
 324             mixer = getMixer(infoArray[i]);
 325 
 326             currentInfoArray = mixer.getTargetLineInfo(info);
 327             for (int j = 0; j < currentInfoArray.length; j++) {
 328                 vector.addElement(currentInfoArray[j]);
 329             }
 330         }
 331 
 332         Line.Info[] returnedArray = new Line.Info[vector.size()];
 333 
 334         for (int i = 0; i < returnedArray.length; i++) {
 335             returnedArray[i] = (Line.Info)vector.get(i);
 336         }
 337 
 338         return returnedArray;
 339     }
 340 
 341 
 342     /**
 343      * Indicates whether the system supports any lines that match
 344      * the specified <code>Line.Info</code> object.  A line is supported if
 345      * any installed mixer supports it.
 346      * @param info a <code>Line.Info</code> object describing the line for which support is queried
 347      * @return <code>true</code> if at least one matching line is
 348      * supported, otherwise <code>false</code>
 349      *
 350      * @see Mixer#isLineSupported(Line.Info)
 351      */
 352     public static boolean isLineSupported(Line.Info info) {
 353 
 354         Mixer mixer;
 355         Mixer.Info[] infoArray = getMixerInfo();
 356 
 357         for (int i = 0; i < infoArray.length; i++) {
 358 
 359             if( infoArray[i] != null ) {
 360                 mixer = getMixer(infoArray[i]);
 361                 if (mixer.isLineSupported(info)) {
 362                     return true;
 363                 }
 364             }
 365         }
 366 
 367         return false;
 368     }
 369 
 370     /**
 371      * Obtains a line that matches the description in the specified
 372      * <code>Line.Info</code> object.
 373      *
 374      * <p>If a <code>DataLine</code> is requested, and <code>info</code>
 375      * is an instance of <code>DataLine.Info</code> specifying at least
 376      * one fully qualified audio format, the last one
 377      * will be used as the default format of the returned
 378      * <code>DataLine</code>.
 379      *
 380      * <p>If system properties
 381      * <code>javax.sound.sampled.Clip</code>,
 382      * <code>javax.sound.sampled.Port</code>,
 383      * <code>javax.sound.sampled.SourceDataLine</code> and
 384      * <code>javax.sound.sampled.TargetDataLine</code> are defined
 385      * or they are defined in the file &quot;sound.properties&quot;,
 386      * they are used to retrieve default lines.
 387      * For details, refer to the {@link AudioSystem class description}.
 388      *
 389      * If the respective property is not set, or the mixer
 390      * requested in the property is not installed or does not provide the
 391      * requested line, all installed mixers are queried for the
 392      * requested line type. A Line will be returned from the first mixer
 393      * providing the requested line type.
 394      *
 395      * @param info a <code>Line.Info</code> object describing the desired kind of line
 396      * @return a line of the requested kind
 397      *
 398      * @throws LineUnavailableException if a matching line
 399      * is not available due to resource restrictions
 400      * @throws SecurityException if a matching line
 401      * is not available due to security restrictions
 402      * @throws IllegalArgumentException if the system does not
 403      * support at least one line matching the specified
 404      * <code>Line.Info</code> object
 405      * through any installed mixer
 406      */
 407     public static Line getLine(Line.Info info) throws LineUnavailableException {
 408         LineUnavailableException lue = null;
 409         List providers = getMixerProviders();
 410 
 411 
 412         // 1: try from default mixer for this line class
 413         try {
 414             Mixer mixer = getDefaultMixer(providers, info);
 415             if (mixer != null && mixer.isLineSupported(info)) {
 416                 return mixer.getLine(info);
 417             }
 418         } catch (LineUnavailableException e) {
 419             lue = e;
 420         } catch (IllegalArgumentException iae) {
 421             // must not happen... but better to catch it here,
 422             // if plug-ins are badly written
 423         }
 424 
 425 
 426         // 2: if that doesn't work, try to find any mixing mixer
 427         for(int i = 0; i < providers.size(); i++) {
 428             MixerProvider provider = (MixerProvider) providers.get(i);
 429             Mixer.Info[] infos = provider.getMixerInfo();
 430 
 431             for (int j = 0; j < infos.length; j++) {
 432                 try {
 433                     Mixer mixer = provider.getMixer(infos[j]);
 434                     // see if this is an appropriate mixer which can mix
 435                     if (isAppropriateMixer(mixer, info, true)) {
 436                         return mixer.getLine(info);
 437                     }
 438                 } catch (LineUnavailableException e) {
 439                     lue = e;
 440                 } catch (IllegalArgumentException iae) {
 441                     // must not happen... but better to catch it here,
 442                     // if plug-ins are badly written
 443                 }
 444             }
 445         }
 446 
 447 
 448         // 3: if that didn't work, try to find any non-mixing mixer
 449         for(int i = 0; i < providers.size(); i++) {
 450             MixerProvider provider = (MixerProvider) providers.get(i);
 451             Mixer.Info[] infos = provider.getMixerInfo();
 452             for (int j = 0; j < infos.length; j++) {
 453                 try {
 454                     Mixer mixer = provider.getMixer(infos[j]);
 455                     // see if this is an appropriate mixer which can mix
 456                     if (isAppropriateMixer(mixer, info, false)) {
 457                         return mixer.getLine(info);
 458                     }
 459                 } catch (LineUnavailableException e) {
 460                     lue = e;
 461                 } catch (IllegalArgumentException iae) {
 462                     // must not happen... but better to catch it here,
 463                     // if plug-ins are badly written
 464                 }
 465             }
 466         }
 467 
 468         // if this line was supported but was not available, throw the last
 469         // LineUnavailableException we got (??).
 470         if (lue != null) {
 471             throw lue;
 472         }
 473 
 474         // otherwise, the requested line was not supported, so throw
 475         // an Illegal argument exception
 476         throw new IllegalArgumentException("No line matching " +
 477                                            info.toString() + " is supported.");
 478     }
 479 
 480 
 481     /**
 482      * Obtains a clip that can be used for playing back
 483      * an audio file or an audio stream. The returned clip
 484      * will be provided by the default system mixer, or,
 485      * if not possible, by any other mixer installed in the
 486      * system that supports a <code>Clip</code>
 487      * object.
 488      *
 489      * <p>The returned clip must be opened with the
 490      * <code>open(AudioFormat)</code> or
 491      * <code>open(AudioInputStream)</code> method.
 492      *
 493      * <p>This is a high-level method that uses <code>getMixer</code>
 494      * and <code>getLine</code> internally.
 495      *
 496      * <p>If the system property
 497      * <code>javax.sound.sampled.Clip</code>
 498      * is defined or it is defined in the file &quot;sound.properties&quot;,
 499      * it is used to retrieve the default clip.
 500      * For details, refer to the {@link AudioSystem class description}.
 501      *
 502      * @return the desired clip object
 503      *
 504      * @throws LineUnavailableException if a clip object
 505      * is not available due to resource restrictions
 506      * @throws SecurityException if a clip object
 507      * is not available due to security restrictions
 508      * @throws IllegalArgumentException if the system does not
 509      * support at least one clip instance through any installed mixer
 510      *
 511      * @see #getClip(Mixer.Info)
 512      * @since 1.5
 513      */
 514     public static Clip getClip() throws LineUnavailableException{
 515         AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
 516                                              AudioSystem.NOT_SPECIFIED,
 517                                              16, 2, 4,
 518                                              AudioSystem.NOT_SPECIFIED, true);
 519         DataLine.Info info = new DataLine.Info(Clip.class, format);
 520         return (Clip) AudioSystem.getLine(info);
 521     }
 522 
 523 
 524     /**
 525      * Obtains a clip from the specified mixer that can be
 526      * used for playing back an audio file or an audio stream.
 527      *
 528      * <p>The returned clip must be opened with the
 529      * <code>open(AudioFormat)</code> or
 530      * <code>open(AudioInputStream)</code> method.
 531      *
 532      * <p>This is a high-level method that uses <code>getMixer</code>
 533      * and <code>getLine</code> internally.
 534      *
 535      * @param mixerInfo a <code>Mixer.Info</code> object representing the
 536      * desired mixer, or <code>null</code> for the system default mixer
 537      * @return a clip object from the specified mixer
 538      *
 539      * @throws LineUnavailableException if a clip
 540      * is not available from this mixer due to resource restrictions
 541      * @throws SecurityException if a clip
 542      * is not available from this mixer due to security restrictions
 543      * @throws IllegalArgumentException if the system does not
 544      * support at least one clip through the specified mixer
 545      *
 546      * @see #getClip()
 547      * @since 1.5
 548      */
 549     public static Clip getClip(Mixer.Info mixerInfo) throws LineUnavailableException{
 550         AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
 551                                              AudioSystem.NOT_SPECIFIED,
 552                                              16, 2, 4,
 553                                              AudioSystem.NOT_SPECIFIED, true);
 554         DataLine.Info info = new DataLine.Info(Clip.class, format);
 555         Mixer mixer = AudioSystem.getMixer(mixerInfo);
 556         return (Clip) mixer.getLine(info);
 557     }
 558 
 559 
 560     /**
 561      * Obtains a source data line that can be used for playing back
 562      * audio data in the format specified by the
 563      * <code>AudioFormat</code> object. The returned line
 564      * will be provided by the default system mixer, or,
 565      * if not possible, by any other mixer installed in the
 566      * system that supports a matching
 567      * <code>SourceDataLine</code> object.
 568      *
 569      * <p>The returned line should be opened with the
 570      * <code>open(AudioFormat)</code> or
 571      * <code>open(AudioFormat, int)</code> method.
 572      *
 573      * <p>This is a high-level method that uses <code>getMixer</code>
 574      * and <code>getLine</code> internally.
 575      *
 576      * <p>The returned <code>SourceDataLine</code>'s default
 577      * audio format will be initialized with <code>format</code>.
 578      *
 579      * <p>If the system property
 580      * <code>javax.sound.sampled.SourceDataLine</code>
 581      * is defined or it is defined in the file &quot;sound.properties&quot;,
 582      * it is used to retrieve the default source data line.
 583      * For details, refer to the {@link AudioSystem class description}.
 584      *
 585      * @param format an <code>AudioFormat</code> object specifying
 586      *        the supported audio format of the returned line,
 587      *        or <code>null</code> for any audio format
 588      * @return the desired <code>SourceDataLine</code> object
 589      *
 590      * @throws LineUnavailableException if a matching source data line
 591      *         is not available due to resource restrictions
 592      * @throws SecurityException if a matching source data line
 593      *         is not available due to security restrictions
 594      * @throws IllegalArgumentException if the system does not
 595      *         support at least one source data line supporting the
 596      *         specified audio format through any installed mixer
 597      *
 598      * @see #getSourceDataLine(AudioFormat, Mixer.Info)
 599      * @since 1.5
 600      */
 601     public static SourceDataLine getSourceDataLine(AudioFormat format)
 602         throws LineUnavailableException{
 603         DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
 604         return (SourceDataLine) AudioSystem.getLine(info);
 605     }
 606 
 607 
 608     /**
 609      * Obtains a source data line that can be used for playing back
 610      * audio data in the format specified by the
 611      * <code>AudioFormat</code> object, provided by the mixer
 612      * specified by the <code>Mixer.Info</code> object.
 613      *
 614      * <p>The returned line should be opened with the
 615      * <code>open(AudioFormat)</code> or
 616      * <code>open(AudioFormat, int)</code> method.
 617      *
 618      * <p>This is a high-level method that uses <code>getMixer</code>
 619      * and <code>getLine</code> internally.
 620      *
 621      * <p>The returned <code>SourceDataLine</code>'s default
 622      * audio format will be initialized with <code>format</code>.
 623      *
 624      * @param format an <code>AudioFormat</code> object specifying
 625      *        the supported audio format of the returned line,
 626      *        or <code>null</code> for any audio format
 627      * @param mixerinfo a <code>Mixer.Info</code> object representing
 628      *        the desired mixer, or <code>null</code> for the system
 629      *        default mixer
 630      * @return the desired <code>SourceDataLine</code> object
 631      *
 632      * @throws LineUnavailableException if a matching source data
 633      *         line is not available from the specified mixer due
 634      *         to resource restrictions
 635      * @throws SecurityException if a matching source data line
 636      *         is not available from the specified mixer due to
 637      *         security restrictions
 638      * @throws IllegalArgumentException if the specified mixer does
 639      *         not support at least one source data line supporting
 640      *         the specified audio format
 641      *
 642      * @see #getSourceDataLine(AudioFormat)
 643      * @since 1.5
 644      */
 645     public static SourceDataLine getSourceDataLine(AudioFormat format,
 646                                                    Mixer.Info mixerinfo)
 647         throws LineUnavailableException{
 648         DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
 649         Mixer mixer = AudioSystem.getMixer(mixerinfo);
 650         return (SourceDataLine) mixer.getLine(info);
 651   }
 652 
 653 
 654     /**
 655      * Obtains a target data line that can be used for recording
 656      * audio data in the format specified by the
 657      * <code>AudioFormat</code> object. The returned line
 658      * will be provided by the default system mixer, or,
 659      * if not possible, by any other mixer installed in the
 660      * system that supports a matching
 661      * <code>TargetDataLine</code> object.
 662      *
 663      * <p>The returned line should be opened with the
 664      * <code>open(AudioFormat)</code> or
 665      * <code>open(AudioFormat, int)</code> method.
 666      *
 667      * <p>This is a high-level method that uses <code>getMixer</code>
 668      * and <code>getLine</code> internally.
 669      *
 670      * <p>The returned <code>TargetDataLine</code>'s default
 671      * audio format will be initialized with <code>format</code>.
 672      *
 673      * <p>If the system property
 674      * {@code javax.sound.sampled.TargetDataLine}
 675      * is defined or it is defined in the file &quot;sound.properties&quot;,
 676      * it is used to retrieve the default target data line.
 677      * For details, refer to the {@link AudioSystem class description}.
 678      *
 679      * @param format an <code>AudioFormat</code> object specifying
 680      *        the supported audio format of the returned line,
 681      *        or <code>null</code> for any audio format
 682      * @return the desired <code>TargetDataLine</code> object
 683      *
 684      * @throws LineUnavailableException if a matching target data line
 685      *         is not available due to resource restrictions
 686      * @throws SecurityException if a matching target data line
 687      *         is not available due to security restrictions
 688      * @throws IllegalArgumentException if the system does not
 689      *         support at least one target data line supporting the
 690      *         specified audio format through any installed mixer
 691      *
 692      * @see #getTargetDataLine(AudioFormat, Mixer.Info)
 693      * @see AudioPermission
 694      * @since 1.5
 695      */
 696     public static TargetDataLine getTargetDataLine(AudioFormat format)
 697         throws LineUnavailableException{
 698 
 699         DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
 700         return (TargetDataLine) AudioSystem.getLine(info);
 701     }
 702 
 703 
 704 
 705     /**
 706      * Obtains a target data line that can be used for recording
 707      * audio data in the format specified by the
 708      * <code>AudioFormat</code> object, provided by the mixer
 709      * specified by the <code>Mixer.Info</code> object.
 710      *
 711      * <p>The returned line should be opened with the
 712      * <code>open(AudioFormat)</code> or
 713      * <code>open(AudioFormat, int)</code> method.
 714      *
 715      * <p>This is a high-level method that uses <code>getMixer</code>
 716      * and <code>getLine</code> internally.
 717      *
 718      * <p>The returned <code>TargetDataLine</code>'s default
 719      * audio format will be initialized with <code>format</code>.
 720      *
 721      * @param format an <code>AudioFormat</code> object specifying
 722      *        the supported audio format of the returned line,
 723      *        or <code>null</code> for any audio format
 724      * @param mixerinfo a <code>Mixer.Info</code> object representing the
 725      *        desired mixer, or <code>null</code> for the system default mixer
 726      * @return the desired <code>TargetDataLine</code> object
 727      *
 728      * @throws LineUnavailableException if a matching target data
 729      *         line is not available from the specified mixer due
 730      *         to resource restrictions
 731      * @throws SecurityException if a matching target data line
 732      *         is not available from the specified mixer due to
 733      *         security restrictions
 734      * @throws IllegalArgumentException if the specified mixer does
 735      *         not support at least one target data line supporting
 736      *         the specified audio format
 737      *
 738      * @see #getTargetDataLine(AudioFormat)
 739      * @see AudioPermission
 740      * @since 1.5
 741      */
 742     public static TargetDataLine getTargetDataLine(AudioFormat format,
 743                                                    Mixer.Info mixerinfo)
 744         throws LineUnavailableException {
 745 
 746         DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
 747         Mixer mixer = AudioSystem.getMixer(mixerinfo);
 748         return (TargetDataLine) mixer.getLine(info);
 749     }
 750 
 751 
 752     // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec
 753     /**
 754      * Obtains the encodings that the system can obtain from an
 755      * audio input stream with the specified encoding using the set
 756      * of installed format converters.
 757      * @param sourceEncoding the encoding for which conversion support
 758      * is queried
 759      * @return array of encodings.  If <code>sourceEncoding</code>is not supported,
 760      * an array of length 0 is returned. Otherwise, the array will have a length
 761      * of at least 1, representing <code>sourceEncoding</code> (no conversion).
 762      */
 763     public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat.Encoding sourceEncoding) {
 764 
 765         List codecs = getFormatConversionProviders();
 766         Vector encodings = new Vector();
 767 
 768         AudioFormat.Encoding encs[] = null;
 769 
 770         // gather from all the codecs
 771         for(int i=0; i<codecs.size(); i++ ) {
 772             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 773             if( codec.isSourceEncodingSupported( sourceEncoding ) ) {
 774                 encs = codec.getTargetEncodings();
 775                 for (int j = 0; j < encs.length; j++) {
 776                     encodings.addElement( encs[j] );
 777                 }
 778             }
 779         }
 780         AudioFormat.Encoding encs2[] = (AudioFormat.Encoding[]) encodings.toArray(new AudioFormat.Encoding[0]);
 781         return encs2;
 782     }
 783 
 784 
 785 
 786     // $$fb 2002-04-12: fix for 4662082: behavior of AudioSystem.getTargetEncodings() methods doesn't match the spec
 787     /**
 788      * Obtains the encodings that the system can obtain from an
 789      * audio input stream with the specified format using the set
 790      * of installed format converters.
 791      * @param sourceFormat the audio format for which conversion
 792      * is queried
 793      * @return array of encodings. If <code>sourceFormat</code>is not supported,
 794      * an array of length 0 is returned. Otherwise, the array will have a length
 795      * of at least 1, representing the encoding of <code>sourceFormat</code> (no conversion).
 796      */
 797     public static AudioFormat.Encoding[] getTargetEncodings(AudioFormat sourceFormat) {
 798 
 799 
 800         List codecs = getFormatConversionProviders();
 801         Vector encodings = new Vector();
 802 
 803         int size = 0;
 804         int index = 0;
 805         AudioFormat.Encoding encs[] = null;
 806 
 807         // gather from all the codecs
 808 
 809         for(int i=0; i<codecs.size(); i++ ) {
 810             encs = ((FormatConversionProvider) codecs.get(i)).getTargetEncodings(sourceFormat);
 811             size += encs.length;
 812             encodings.addElement( encs );
 813         }
 814 
 815         // now build a new array
 816 
 817         AudioFormat.Encoding encs2[] = new AudioFormat.Encoding[size];
 818         for(int i=0; i<encodings.size(); i++ ) {
 819             encs = (AudioFormat.Encoding [])(encodings.get(i));
 820             for(int j=0; j<encs.length; j++ ) {
 821                 encs2[index++] = encs[j];
 822             }
 823         }
 824         return encs2;
 825     }
 826 
 827 
 828     /**
 829      * Indicates whether an audio input stream of the specified encoding
 830      * can be obtained from an audio input stream that has the specified
 831      * format.
 832      * @param targetEncoding the desired encoding after conversion
 833      * @param sourceFormat the audio format before conversion
 834      * @return <code>true</code> if the conversion is supported,
 835      * otherwise <code>false</code>
 836      */
 837     public static boolean isConversionSupported(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {
 838 
 839 
 840         List codecs = getFormatConversionProviders();
 841 
 842         for(int i=0; i<codecs.size(); i++ ) {
 843             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 844             if(codec.isConversionSupported(targetEncoding,sourceFormat) ) {
 845                 return true;
 846             }
 847         }
 848         return false;
 849     }
 850 
 851 
 852     /**
 853      * Obtains an audio input stream of the indicated encoding, by converting the
 854      * provided audio input stream.
 855      * @param targetEncoding the desired encoding after conversion
 856      * @param sourceStream the stream to be converted
 857      * @return an audio input stream of the indicated encoding
 858      * @throws IllegalArgumentException if the conversion is not supported
 859      * @see #getTargetEncodings(AudioFormat.Encoding)
 860      * @see #getTargetEncodings(AudioFormat)
 861      * @see #isConversionSupported(AudioFormat.Encoding, AudioFormat)
 862      * @see #getAudioInputStream(AudioFormat, AudioInputStream)
 863      */
 864     public static AudioInputStream getAudioInputStream(AudioFormat.Encoding targetEncoding,
 865                                                        AudioInputStream sourceStream) {
 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( targetEncoding, sourceStream.getFormat() ) ) {
 872                 return codec.getAudioInputStream( targetEncoding, sourceStream );
 873             }
 874         }
 875         // we ran out of options, throw an exception
 876         throw new IllegalArgumentException("Unsupported conversion: " + targetEncoding + " from " + sourceStream.getFormat());
 877     }
 878 
 879 
 880     /**
 881      * Obtains the formats that have a particular encoding and that the system can
 882      * obtain from a stream of the specified format using the set of
 883      * installed format converters.
 884      * @param targetEncoding the desired encoding after conversion
 885      * @param sourceFormat the audio format before conversion
 886      * @return array of formats.  If no formats of the specified
 887      * encoding are supported, an array of length 0 is returned.
 888      */
 889     public static AudioFormat[] getTargetFormats(AudioFormat.Encoding targetEncoding, AudioFormat sourceFormat) {
 890 
 891         List codecs = getFormatConversionProviders();
 892         Vector formats = new Vector();
 893 
 894         int size = 0;
 895         int index = 0;
 896         AudioFormat fmts[] = null;
 897 
 898         // gather from all the codecs
 899 
 900         for(int i=0; i<codecs.size(); i++ ) {
 901             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 902             fmts = codec.getTargetFormats(targetEncoding, sourceFormat);
 903             size += fmts.length;
 904             formats.addElement( fmts );
 905         }
 906 
 907         // now build a new array
 908 
 909         AudioFormat fmts2[] = new AudioFormat[size];
 910         for(int i=0; i<formats.size(); i++ ) {
 911             fmts = (AudioFormat [])(formats.get(i));
 912             for(int j=0; j<fmts.length; j++ ) {
 913                 fmts2[index++] = fmts[j];
 914             }
 915         }
 916         return fmts2;
 917     }
 918 
 919 
 920     /**
 921      * Indicates whether an audio input stream of a specified format
 922      * can be obtained from an audio input stream of another specified format.
 923      * @param targetFormat the desired audio format after conversion
 924      * @param sourceFormat the audio format before conversion
 925      * @return <code>true</code> if the conversion is supported,
 926      * otherwise <code>false</code>
 927      */
 928 
 929     public static boolean isConversionSupported(AudioFormat targetFormat, AudioFormat sourceFormat) {
 930 
 931         List codecs = getFormatConversionProviders();
 932 
 933         for(int i=0; i<codecs.size(); i++ ) {
 934             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 935             if(codec.isConversionSupported(targetFormat, sourceFormat) ) {
 936                 return true;
 937             }
 938         }
 939         return false;
 940     }
 941 
 942 
 943     /**
 944      * Obtains an audio input stream of the indicated format, by converting the
 945      * provided audio input stream.
 946      * @param targetFormat the desired audio format after conversion
 947      * @param sourceStream the stream to be converted
 948      * @return an audio input stream of the indicated format
 949      * @throws IllegalArgumentException if the conversion is not supported
 950      * #see #getTargetEncodings(AudioFormat)
 951      * @see #getTargetFormats(AudioFormat.Encoding, AudioFormat)
 952      * @see #isConversionSupported(AudioFormat, AudioFormat)
 953      * @see #getAudioInputStream(AudioFormat.Encoding, AudioInputStream)
 954      */
 955     public static AudioInputStream getAudioInputStream(AudioFormat targetFormat,
 956                                                        AudioInputStream sourceStream) {
 957 
 958         if (sourceStream.getFormat().matches(targetFormat)) {
 959             return sourceStream;
 960         }
 961 
 962         List codecs = getFormatConversionProviders();
 963 
 964         for(int i = 0; i < codecs.size(); i++) {
 965             FormatConversionProvider codec = (FormatConversionProvider) codecs.get(i);
 966             if(codec.isConversionSupported(targetFormat,sourceStream.getFormat()) ) {
 967                 return codec.getAudioInputStream(targetFormat,sourceStream);
 968             }
 969         }
 970 
 971         // we ran out of options...
 972         throw new IllegalArgumentException("Unsupported conversion: " + targetFormat + " from " + sourceStream.getFormat());
 973     }
 974 
 975 
 976     /**
 977      * Obtains the audio file format of the provided input stream.  The stream must
 978      * point to valid audio file data.  The implementation of this method may require
 979      * multiple parsers to examine the stream to determine whether they support it.
 980      * These parsers must be able to mark the stream, read enough data to determine whether they
 981      * support the stream, and, if not, reset the stream's read pointer to its original
 982      * position.  If the input stream does not support these operations, this method may fail
 983      * with an <code>IOException</code>.
 984      * @param stream the input stream from which file format information should be
 985      * extracted
 986      * @return an <code>AudioFileFormat</code> object describing the stream's audio file format
 987      * @throws UnsupportedAudioFileException if the stream does not point to valid audio
 988      * file data recognized by the system
 989      * @throws IOException if an input/output exception occurs
 990      * @see InputStream#markSupported
 991      * @see InputStream#mark
 992      */
 993     public static AudioFileFormat getAudioFileFormat(InputStream stream)
 994         throws UnsupportedAudioFileException, IOException {
 995 
 996         List providers = getAudioFileReaders();
 997         AudioFileFormat format = null;
 998 
 999         for(int i = 0; i < providers.size(); i++ ) {
1000             AudioFileReader reader = (AudioFileReader) providers.get(i);
1001             try {
1002                 format = reader.getAudioFileFormat( stream ); // throws IOException
1003                 break;
1004             } catch (UnsupportedAudioFileException e) {
1005                 continue;
1006             }
1007         }
1008 
1009         if( format==null ) {
1010             throw new UnsupportedAudioFileException("file is not a supported file type");
1011         } else {
1012             return format;
1013         }
1014     }
1015 
1016     /**
1017      * Obtains the audio file format of the specified URL.  The URL must
1018      * point to valid audio file data.
1019      * @param url the URL from which file format information should be
1020      * extracted
1021      * @return an <code>AudioFileFormat</code> object describing the audio file format
1022      * @throws UnsupportedAudioFileException if the URL does not point to valid audio
1023      * file data recognized by the system
1024      * @throws IOException if an input/output exception occurs
1025      */
1026     public static AudioFileFormat getAudioFileFormat(URL url)
1027         throws UnsupportedAudioFileException, IOException {
1028 
1029         List providers = getAudioFileReaders();
1030         AudioFileFormat format = null;
1031 
1032         for(int i = 0; i < providers.size(); i++ ) {
1033             AudioFileReader reader = (AudioFileReader) providers.get(i);
1034             try {
1035                 format = reader.getAudioFileFormat( url ); // throws IOException
1036                 break;
1037             } catch (UnsupportedAudioFileException e) {
1038                 continue;
1039             }
1040         }
1041 
1042         if( format==null ) {
1043             throw new UnsupportedAudioFileException("file is not a supported file type");
1044         } else {
1045             return format;
1046         }
1047     }
1048 
1049     /**
1050      * Obtains the audio file format of the specified <code>File</code>.  The <code>File</code> must
1051      * point to valid audio file data.
1052      * @param file the <code>File</code> from which file format information should be
1053      * extracted
1054      * @return an <code>AudioFileFormat</code> object describing the audio file format
1055      * @throws UnsupportedAudioFileException if the <code>File</code> does not point to valid audio
1056      * file data recognized by the system
1057      * @throws IOException if an I/O exception occurs
1058      */
1059     public static AudioFileFormat getAudioFileFormat(File file)
1060         throws UnsupportedAudioFileException, IOException {
1061 
1062         List providers = getAudioFileReaders();
1063         AudioFileFormat format = null;
1064 
1065         for(int i = 0; i < providers.size(); i++ ) {
1066             AudioFileReader reader = (AudioFileReader) providers.get(i);
1067             try {
1068                 format = reader.getAudioFileFormat( file ); // throws IOException
1069                 break;
1070             } catch (UnsupportedAudioFileException e) {
1071                 continue;
1072             }
1073         }
1074 
1075         if( format==null ) {
1076             throw new UnsupportedAudioFileException("file is not a supported file type");
1077         } else {
1078             return format;
1079         }
1080     }
1081 
1082 
1083     /**
1084      * Obtains an audio input stream from the provided input stream.  The stream must
1085      * point to valid audio file data.  The implementation of this method may
1086      * require multiple parsers to
1087      * examine the stream to determine whether they support it.  These parsers must
1088      * be able to mark the stream, read enough data to determine whether they
1089      * support the stream, and, if not, reset the stream's read pointer to its original
1090      * position.  If the input stream does not support these operation, this method may fail
1091      * with an <code>IOException</code>.
1092      * @param stream the input stream from which the <code>AudioInputStream</code> should be
1093      * constructed
1094      * @return an <code>AudioInputStream</code> object based on the audio file data contained
1095      * in the input stream.
1096      * @throws UnsupportedAudioFileException if the stream does not point to valid audio
1097      * file data recognized by the system
1098      * @throws IOException if an I/O exception occurs
1099      * @see InputStream#markSupported
1100      * @see InputStream#mark
1101      */
1102     public static AudioInputStream getAudioInputStream(InputStream stream)
1103         throws UnsupportedAudioFileException, IOException {
1104 
1105         List providers = getAudioFileReaders();
1106         AudioInputStream audioStream = null;
1107 
1108         for(int i = 0; i < providers.size(); i++ ) {
1109             AudioFileReader reader = (AudioFileReader) providers.get(i);
1110             try {
1111                 audioStream = reader.getAudioInputStream( stream ); // throws IOException
1112                 break;
1113             } catch (UnsupportedAudioFileException e) {
1114                 continue;
1115             }
1116         }
1117 
1118         if( audioStream==null ) {
1119             throw new UnsupportedAudioFileException("could not get audio input stream from input stream");
1120         } else {
1121             return audioStream;
1122         }
1123     }
1124 
1125     /**
1126      * Obtains an audio input stream from the URL provided.  The URL must
1127      * point to valid audio file data.
1128      * @param url the URL for which the <code>AudioInputStream</code> should be
1129      * constructed
1130      * @return an <code>AudioInputStream</code> object based on the audio file data pointed
1131      * to by the URL
1132      * @throws UnsupportedAudioFileException if the URL does not point to valid audio
1133      * file data recognized by the system
1134      * @throws IOException if an I/O exception occurs
1135      */
1136     public static AudioInputStream getAudioInputStream(URL url)
1137         throws UnsupportedAudioFileException, IOException {
1138 
1139         List providers = getAudioFileReaders();
1140         AudioInputStream audioStream = null;
1141 
1142         for(int i = 0; i < providers.size(); i++ ) {
1143             AudioFileReader reader = (AudioFileReader) providers.get(i);
1144             try {
1145                 audioStream = reader.getAudioInputStream( url ); // throws IOException
1146                 break;
1147             } catch (UnsupportedAudioFileException e) {
1148                 continue;
1149             }
1150         }
1151 
1152         if( audioStream==null ) {
1153             throw new UnsupportedAudioFileException("could not get audio input stream from input URL");
1154         } else {
1155             return audioStream;
1156         }
1157     }
1158 
1159     /**
1160      * Obtains an audio input stream from the provided <code>File</code>.  The <code>File</code> must
1161      * point to valid audio file data.
1162      * @param file the <code>File</code> for which the <code>AudioInputStream</code> should be
1163      * constructed
1164      * @return an <code>AudioInputStream</code> object based on the audio file data pointed
1165      * to by the <code>File</code>
1166      * @throws UnsupportedAudioFileException if the <code>File</code> does not point to valid audio
1167      * file data recognized by the system
1168      * @throws IOException if an I/O exception occurs
1169      */
1170     public static AudioInputStream getAudioInputStream(File file)
1171         throws UnsupportedAudioFileException, IOException {
1172 
1173         List providers = getAudioFileReaders();
1174         AudioInputStream audioStream = null;
1175 
1176         for(int i = 0; i < providers.size(); i++ ) {
1177             AudioFileReader reader = (AudioFileReader) providers.get(i);
1178             try {
1179                 audioStream = reader.getAudioInputStream( file ); // throws IOException
1180                 break;
1181             } catch (UnsupportedAudioFileException e) {
1182                 continue;
1183             }
1184         }
1185 
1186         if( audioStream==null ) {
1187             throw new UnsupportedAudioFileException("could not get audio input stream from input file");
1188         } else {
1189             return audioStream;
1190         }
1191     }
1192 
1193 
1194     /**
1195      * Obtains the file types for which file writing support is provided by the system.
1196      * @return array of unique file types.  If no file types are supported,
1197      * an array of length 0 is returned.
1198      */
1199     public static AudioFileFormat.Type[] getAudioFileTypes() {
1200         List providers = getAudioFileWriters();
1201         Set returnTypesSet = new HashSet();
1202 
1203         for(int i=0; i < providers.size(); i++) {
1204             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1205             AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes();
1206             for(int j=0; j < fileTypes.length; j++) {
1207                 returnTypesSet.add(fileTypes[j]);
1208             }
1209         }
1210         AudioFileFormat.Type returnTypes[] = (AudioFileFormat.Type[])
1211             returnTypesSet.toArray(new AudioFileFormat.Type[0]);
1212         return returnTypes;
1213     }
1214 
1215 
1216     /**
1217      * Indicates whether file writing support for the specified file type is provided
1218      * by the system.
1219      * @param fileType the file type for which write capabilities are queried
1220      * @return <code>true</code> if the file type is supported,
1221      * otherwise <code>false</code>
1222      */
1223     public static boolean isFileTypeSupported(AudioFileFormat.Type fileType) {
1224 
1225         List providers = getAudioFileWriters();
1226 
1227         for(int i=0; i < providers.size(); i++) {
1228             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1229             if (writer.isFileTypeSupported(fileType)) {
1230                 return true;
1231             }
1232         }
1233         return false;
1234     }
1235 
1236 
1237     /**
1238      * Obtains the file types that the system can write from the
1239      * audio input stream specified.
1240      * @param stream the audio input stream for which audio file type support
1241      * is queried
1242      * @return array of file types.  If no file types are supported,
1243      * an array of length 0 is returned.
1244      */
1245     public static AudioFileFormat.Type[] getAudioFileTypes(AudioInputStream stream) {
1246         List providers = getAudioFileWriters();
1247         Set returnTypesSet = new HashSet();
1248 
1249         for(int i=0; i < providers.size(); i++) {
1250             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1251             AudioFileFormat.Type[] fileTypes = writer.getAudioFileTypes(stream);
1252             for(int j=0; j < fileTypes.length; j++) {
1253                 returnTypesSet.add(fileTypes[j]);
1254             }
1255         }
1256         AudioFileFormat.Type returnTypes[] = (AudioFileFormat.Type[])
1257             returnTypesSet.toArray(new AudioFileFormat.Type[0]);
1258         return returnTypes;
1259     }
1260 
1261 
1262     /**
1263      * Indicates whether an audio file of the specified file type can be written
1264      * from the indicated audio input stream.
1265      * @param fileType the file type for which write capabilities are queried
1266      * @param stream the stream for which file-writing support is queried
1267      * @return <code>true</code> if the file type is supported for this audio input stream,
1268      * otherwise <code>false</code>
1269      */
1270     public static boolean isFileTypeSupported(AudioFileFormat.Type fileType,
1271                                               AudioInputStream stream) {
1272 
1273         List providers = getAudioFileWriters();
1274 
1275         for(int i=0; i < providers.size(); i++) {
1276             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1277             if(writer.isFileTypeSupported(fileType, stream)) {
1278                 return true;
1279             }
1280         }
1281         return false;
1282     }
1283 
1284 
1285     /**
1286      * Writes a stream of bytes representing an audio file of the specified file type
1287      * to the output stream provided.  Some file types require that
1288      * the length be written into the file header; such files cannot be written from
1289      * start to finish unless the length is known in advance.  An attempt
1290      * to write a file of such a type will fail with an IOException if the length in
1291      * the audio file type is <code>AudioSystem.NOT_SPECIFIED</code>.
1292      *
1293      * @param stream the audio input stream containing audio data to be
1294      * written to the file
1295      * @param fileType the kind of audio file to write
1296      * @param out the stream to which the file data should be written
1297      * @return the number of bytes written to the output stream
1298      * @throws IOException if an input/output exception occurs
1299      * @throws IllegalArgumentException if the file type is not supported by
1300      * the system
1301      * @see #isFileTypeSupported
1302      * @see     #getAudioFileTypes
1303      */
1304     public static int write(AudioInputStream stream, AudioFileFormat.Type fileType,
1305                             OutputStream out) throws IOException {
1306 
1307         List providers = getAudioFileWriters();
1308         int bytesWritten = 0;
1309         boolean flag = false;
1310 
1311         for(int i=0; i < providers.size(); i++) {
1312             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1313             try {
1314                 bytesWritten = writer.write( stream, fileType, out ); // throws IOException
1315                 flag = true;
1316                 break;
1317             } catch (IllegalArgumentException e) {
1318                 // thrown if this provider cannot write the sequence, try the next
1319                 continue;
1320             }
1321         }
1322         if(!flag) {
1323             throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType);
1324         } else {
1325             return bytesWritten;
1326         }
1327     }
1328 
1329 
1330     /**
1331      * Writes a stream of bytes representing an audio file of the specified file type
1332      * to the external file provided.
1333      * @param stream the audio input stream containing audio data to be
1334      * written to the file
1335      * @param fileType the kind of audio file to write
1336      * @param out the external file to which the file data should be written
1337      * @return the number of bytes written to the file
1338      * @throws IOException if an I/O exception occurs
1339      * @throws IllegalArgumentException if the file type is not supported by
1340      * the system
1341      * @see #isFileTypeSupported
1342      * @see     #getAudioFileTypes
1343      */
1344     public static int write(AudioInputStream stream, AudioFileFormat.Type fileType,
1345                             File out) throws IOException {
1346 
1347         List providers = getAudioFileWriters();
1348         int bytesWritten = 0;
1349         boolean flag = false;
1350 
1351         for(int i=0; i < providers.size(); i++) {
1352             AudioFileWriter writer = (AudioFileWriter) providers.get(i);
1353             try {
1354                 bytesWritten = writer.write( stream, fileType, out ); // throws IOException
1355                 flag = true;
1356                 break;
1357             } catch (IllegalArgumentException e) {
1358                 // thrown if this provider cannot write the sequence, try the next
1359                 continue;
1360             }
1361         }
1362         if (!flag) {
1363             throw new IllegalArgumentException("could not write audio file: file type not supported: " + fileType);
1364         } else {
1365             return bytesWritten;
1366         }
1367     }
1368 
1369 
1370     // METHODS FOR INTERNAL IMPLEMENTATION USE
1371 
1372     /**
1373      * Obtains the set of MixerProviders on the system.
1374      */
1375     private static List getMixerProviders() {
1376         return getProviders(MixerProvider.class);
1377     }
1378 
1379 
1380     /**
1381      * Obtains the set of format converters (codecs, transcoders, etc.)
1382      * that are currently installed on the system.
1383      * @return an array of
1384      * {@link javax.sound.sampled.spi.FormatConversionProvider
1385      * FormatConversionProvider}
1386      * objects representing the available format converters.  If no format
1387      * converters readers are available on the system, an array of length 0 is
1388      * returned.
1389      */
1390     private static List getFormatConversionProviders() {
1391         return getProviders(FormatConversionProvider.class);
1392     }
1393 
1394 
1395     /**
1396      * Obtains the set of audio file readers that are currently installed on the system.
1397      * @return a List of
1398      * {@link javax.sound.sampled.spi.AudioFileReader
1399      * AudioFileReader}
1400      * objects representing the installed audio file readers.  If no audio file
1401      * readers are available on the system, an empty List is returned.
1402      */
1403     private static List getAudioFileReaders() {
1404         return getProviders(AudioFileReader.class);
1405     }
1406 
1407 
1408     /**
1409      * Obtains the set of audio file writers that are currently installed on the system.
1410      * @return a List of
1411      * {@link javax.sound.samples.spi.AudioFileWriter AudioFileWriter}
1412      * objects representing the available audio file writers.  If no audio file
1413      * writers are available on the system, an empty List is returned.
1414      */
1415     private static List getAudioFileWriters() {
1416         return getProviders(AudioFileWriter.class);
1417     }
1418 
1419 
1420 
1421     /** Attempts to locate and return a default Mixer that provides lines
1422      * of the specified type.
1423      *
1424      * @param providers the installed mixer providers
1425      * @param info The requested line type
1426      * TargetDataLine.class, Clip.class or Port.class.
1427      * @return a Mixer that matches the requirements, or null if no default mixer found
1428      */
1429     private static Mixer getDefaultMixer(List providers, Line.Info info) {
1430         Class lineClass = info.getLineClass();
1431         String providerClassName = JDK13Services.getDefaultProviderClassName(lineClass);
1432         String instanceName = JDK13Services.getDefaultInstanceName(lineClass);
1433         Mixer mixer;
1434 
1435         if (providerClassName != null) {
1436             MixerProvider defaultProvider = getNamedProvider(providerClassName, providers);
1437             if (defaultProvider != null) {
1438                 if (instanceName != null) {
1439                     mixer = getNamedMixer(instanceName, defaultProvider, info);
1440                     if (mixer != null) {
1441                         return mixer;
1442                     }
1443                 } else {
1444                     mixer = getFirstMixer(defaultProvider, info,
1445                                           false /* mixing not required*/);
1446                     if (mixer != null) {
1447                         return mixer;
1448                     }
1449                 }
1450 
1451             }
1452         }
1453 
1454         /* Provider class not specified or
1455            provider class cannot be found, or
1456            provider class and instance specified and instance cannot be found or is not appropriate */
1457         if (instanceName != null) {
1458             mixer = getNamedMixer(instanceName, providers, info);
1459             if (mixer != null) {
1460                 return mixer;
1461             }
1462         }
1463 
1464 
1465         /* No default are specified, or if something is specified, everything
1466            failed. */
1467         return null;
1468     }
1469 
1470 
1471 
1472     /** Return a MixerProvider of a given class from the list of
1473         MixerProviders.
1474 
1475         This method never requires the returned Mixer to do mixing.
1476         @param providerClassName The class name of the provider to be returned.
1477         @param providers The list of MixerProviders that is searched.
1478         @return A MixerProvider of the requested class, or null if none is
1479         found.
1480      */
1481     private static MixerProvider getNamedProvider(String providerClassName,
1482                                                   List providers) {
1483         for(int i = 0; i < providers.size(); i++) {
1484             MixerProvider provider = (MixerProvider) providers.get(i);
1485             if (provider.getClass().getName().equals(providerClassName)) {
1486                 return provider;
1487             }
1488         }
1489         return null;
1490     }
1491 
1492 
1493     /** Return a Mixer with a given name from a given MixerProvider.
1494       This method never requires the returned Mixer to do mixing.
1495       @param mixerName The name of the Mixer to be returned.
1496       @param provider The MixerProvider to check for Mixers.
1497       @param info The type of line the returned Mixer is required to
1498       support.
1499 
1500       @return A Mixer matching the requirements, or null if none is found.
1501      */
1502     private static Mixer getNamedMixer(String mixerName,
1503                                        MixerProvider provider,
1504                                        Line.Info info) {
1505         Mixer.Info[] infos = provider.getMixerInfo();
1506         for (int i = 0; i < infos.length; i++) {
1507             if (infos[i].getName().equals(mixerName)) {
1508                 Mixer mixer = provider.getMixer(infos[i]);
1509                 if (isAppropriateMixer(mixer, info, false)) {
1510                     return mixer;
1511                 }
1512             }
1513         }
1514         return null;
1515     }
1516 
1517 
1518     /** From a List of MixerProviders, return a Mixer with a given name.
1519         This method never requires the returned Mixer to do mixing.
1520         @param mixerName The name of the Mixer to be returned.
1521         @param providers The List of MixerProviders to check for Mixers.
1522         @param info The type of line the returned Mixer is required to
1523         support.
1524         @return A Mixer matching the requirements, or null if none is found.
1525      */
1526     private static Mixer getNamedMixer(String mixerName,
1527                                        List providers,
1528                                        Line.Info info) {
1529         for(int i = 0; i < providers.size(); i++) {
1530             MixerProvider provider = (MixerProvider) providers.get(i);
1531             Mixer mixer = getNamedMixer(mixerName, provider, info);
1532             if (mixer != null) {
1533                 return mixer;
1534             }
1535         }
1536         return null;
1537     }
1538 
1539 
1540     /** From a given MixerProvider, return the first appropriate Mixer.
1541         @param provider The MixerProvider to check for Mixers.
1542         @param info The type of line the returned Mixer is required to
1543         support.
1544         @param isMixingRequired If true, only Mixers that support mixing are
1545         returned for line types of SourceDataLine and Clip.
1546 
1547         @return A Mixer that is considered appropriate, or null
1548         if none is found.
1549      */
1550     private static Mixer getFirstMixer(MixerProvider provider,
1551                                        Line.Info info,
1552                                        boolean isMixingRequired) {
1553         Mixer.Info[] infos = provider.getMixerInfo();
1554         for (int j = 0; j < infos.length; j++) {
1555             Mixer mixer = provider.getMixer(infos[j]);
1556             if (isAppropriateMixer(mixer, info, isMixingRequired)) {
1557                 return mixer;
1558             }
1559         }
1560         return null;
1561     }
1562 
1563 
1564     /** Checks if a Mixer is appropriate.
1565         A Mixer is considered appropriate if it support the given line type.
1566         If isMixingRequired is true and the line type is an output one
1567         (SourceDataLine, Clip), the mixer is appropriate if it supports
1568         at least 2 (concurrent) lines of the given type.
1569 
1570         @return true if the mixer is considered appropriate according to the
1571         rules given above, false otherwise.
1572      */
1573     private static boolean isAppropriateMixer(Mixer mixer,
1574                                               Line.Info lineInfo,
1575                                               boolean isMixingRequired) {
1576         if (! mixer.isLineSupported(lineInfo)) {
1577             return false;
1578         }
1579         Class lineClass = lineInfo.getLineClass();
1580         if (isMixingRequired
1581             && (SourceDataLine.class.isAssignableFrom(lineClass) ||
1582                 Clip.class.isAssignableFrom(lineClass))) {
1583             int maxLines = mixer.getMaxLines(lineInfo);
1584             return ((maxLines == NOT_SPECIFIED) || (maxLines > 1));
1585         }
1586         return true;
1587     }
1588 
1589 
1590 
1591     /**
1592      * Like getMixerInfo, but return List
1593      */
1594     private static List getMixerInfoList() {
1595         List providers = getMixerProviders();
1596         return getMixerInfoList(providers);
1597     }
1598 
1599 
1600     /**
1601      * Like getMixerInfo, but return List
1602      */
1603     private static List getMixerInfoList(List providers) {
1604         List infos = new ArrayList();
1605 
1606         Mixer.Info[] someInfos; // per-mixer
1607         Mixer.Info[] allInfos;  // for all mixers
1608 
1609         for(int i = 0; i < providers.size(); i++ ) {
1610             someInfos = (Mixer.Info[])
1611                 ((MixerProvider)providers.get(i)).getMixerInfo();
1612 
1613             for (int j = 0; j < someInfos.length; j++) {
1614                 infos.add(someInfos[j]);
1615             }
1616         }
1617 
1618         return infos;
1619     }
1620 
1621 
1622     /**
1623      * Obtains the set of services currently installed on the system
1624      * using sun.misc.Service, the SPI mechanism in 1.3.
1625      * @return a List of instances of providers for the requested service.
1626      * If no providers are available, a vector of length 0 will be returned.
1627      */
1628     private static List getProviders(Class providerClass) {
1629         return JDK13Services.getProviders(providerClass);
1630     }
1631 }