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